Android Web-View : 将本地 Javascript 文件注入远程网页

IT技术 javascript android caching webview
2021-02-07 12:52:06

之前被问过很多次,我浏览了所有内容,还没有明确的答案。

问题简化:是否可以将本地 Javascript 文件(来自资产或存储)注入到 Android Web 视图中加载的远程网页?我知道可以将此类文件注入 Web 视图中加载的本地网页(资产 HTML)。

为什么我需要这个来工作?:为了让浏览体验更快,避免每次下载较大的文件,如 Js 和 CSS 文件。我想避免 Web 视图缓存。

4个回答

有一种方法可以“强制”从本地资产(例如,assets/js/script.js)注入本地 Javascript 文件,并绕过“不允许加载本地资源”:file:///android_assets/js /script.js ...' 问题。

它类似于另一个线程(Android webview,在 assets 文件夹中加载 javascript 文件)中描述的内容,具有额外的 BASE64 编码/解码用于将您的 Javascript 文件表示为可打印字符串。

我使用的是 Android 4.4.2、API 级别 19 虚拟设备。

下面是一些代码片段:

[资产/js/script.js]:

    'use strict';

    function test() {
       // ... do something
    }

    // more Javascript

[主活动.java]:

    ...

    WebView myWebView = (WebView) findViewById(R.id.webView);
    WebSettings webSettings = myWebView.getSettings();

    webSettings.setJavaScriptEnabled(true);
    webSettings.setAllowUniversalAccessFromFileURLs(true);
    myWebView.setWebViewClient(new WebViewClient() {
       @Override
       public boolean shouldOverrideUrlLoading(WebView view, String url) {
          return false;
       }

       @Override
       public void onPageFinished(WebView view, String url) {
          super.onPageFinished(view, url);

          injectScriptFile(view, "js/script.js"); // see below ...

          // test if the script was loaded
          view.loadUrl("javascript:setTimeout(test(), 500)");
       }

       private void injectScriptFile(WebView view, String scriptFile) {
          InputStream input;
          try {
             input = getAssets().open(scriptFile);
             byte[] buffer = new byte[input.available()];
             input.read(buffer);
             input.close();

             // String-ify the script byte-array using BASE64 encoding !!!
             String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
             view.loadUrl("javascript:(function() {" +
                          "var parent = document.getElementsByTagName('head').item(0);" +
                          "var script = document.createElement('script');" +
                          "script.type = 'text/javascript';" +
             // Tell the browser to BASE64-decode the string into your script !!!
                          "script.innerHTML = window.atob('" + encoded + "');" +
                          "parent.appendChild(script)" +
                          "})()");
          } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
          }
       }
    });

    myWebView.loadUrl("http://www.example.com");

    ...
这是一个很棒的答案!我只有一个问题,它不适用于您要注入的 UTF-8 和 ANSI 字符范围之外的资产。我能够通过将解码更改为以下内容来修复它:“script.innerHTML = decodeURIComponent(escape(window.atob('" + encoding + "')));" +
2021-03-14 12:52:06
@sumit 在 Android 4.4 中,我Runnable按如下方式使用对象:首先,我Handler在 class 中定义了一个MainActivity ... { final Handler handler = new Handler(); ...其次,在我的方法中,我使用了一个Runnable对象,例如void loadWebPage() { final Runnable r = new Runnable() { public void run() { WebView myWebView = ... } }; handler.post(r) }. 我希望它有帮助。
2021-03-15 12:52:06
天才天才!!!你救了我的一天兄弟!我发布了一个悬赏 200 的问题。如果您在下面发布答案,我可以接受您的答案。这是链接:stackoverflow.com/questions/51570906/...
2021-03-16 12:52:06
非常感谢,有效。但是它仅适用于 4.3 及以下的 android 版本。对于 Android 4.4,它说我必须仅在 UI 线程内运行加载项 (loadUrl),当我使用 RunOnUIThread() 执行此操作时,它什么也不做,我的意思是它什么都不加载。你知道可能是什么问题吗?
2021-03-27 12:52:06
我也在做同样的事情,但没有用。原因是,在 Kitkat 版本中,无法使用 view.loadUrl() 在本地注入 js 文件。我不得不使用evaluateJavaScript() 方法并且它起作用了。要注入文件 CSS 文件,可以使用 view.loadUrl(),但对于 js,必须使用evaluateJavaScript()。欢迎使用 ChromeView :)
2021-04-01 12:52:06

loadUrl 仅适用于旧版本,使用evaluateJavascript

webview.evaluateJavascript("(function() { document.getElementsByName('username')[0].value='USERNAME';document.getElementsByName('password')[0].value='PASSWORD'; "+
"return { var1: \"variable1\", var2: \"variable2\" }; })();", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String s) {
                    Log.d("LogName", s); // Prints: {"var1":"variable1","var2":"variable2"}
                }
            });
什么是客户端登录?啊哈...网络视图
2021-03-19 12:52:06
这需要 minSdkVersion 19 或更高版本。
2021-04-07 12:52:06

是的,您可以使用 shouldInterceptRequest() 来拦截远程 url 加载并返回本地存储的内容。

WebView webview = (WebView) findViewById(R.id.webview);

webview.setWebViewClient(new WebViewClient() {
    @Override
    public WebResourceResponse shouldInterceptRequest (final WebView view, String url) {
       if (url.equals("script_url_to_load_local")) {
           return new WebResourceResponse("text/javascript", "UTF-8", new FileInputStream("local_url")));
       } else {
           return super.shouldInterceptRequest(view, url);
       }
    }
});
请注意,shouldInterceptRequest(WebView, String)已被 取代shouldInterceptRequest(WebView, WebResourceRequest)此解决方案仍然有效(除了您必须检查request.getUrl()并记住它的类型是Uri)。另请注意,如果您在WebView.loadData()没有基本 URL 的情况下使用,则不能在 中使用相对路径<script src="...">,似乎WebView忽略了它们。使用loadDataWithBaseURL()来代替。
2021-04-04 12:52:06
这是一个更好的解决方案,因为您可以更一致地运行 Javascript。
2021-04-07 12:52:06

小心使用evaluateJavascript:如果在您的 javascript 中抛出语法错误或异常,它将onReceiveValue使用 null调用您的。支持 SDK 19 和更低版本的最常见方法似乎是这样的:使用 Javascript 在 WebView 中填写表单

此外,如果您非常渴望某种浏览器功能(在我的情况下,永远无法弄清楚如何让 DRM 正常工作),您可以在普通 chrome 中使用书签,只有在您在多功能框中输入书签名称时才有效但确实有效并且确实注入了javascript。

另请注意,默认情况下WebView您不能使用 javascript 警报来测试任何内容,它们不会显示。另请注意,默认情况下的“视频”(如 html <video> 标签)在默认情况下并不“真正有效”,而且 DRM 视频在默认情况下也无效,它们都是配置选项:\