【问题标题】:Android Web-View : Inject local Javascript file to Remote WebpageAndroid Web-View:将本地 Javascript 文件注入远程网页
【发布时间】:2014-02-28 10:44:19
【问题描述】:

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

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

为什么我需要它来工作? :为了让浏览体验更快,避免每次都下载Js和CSS文件等较大的文件。我想避免 Web-View 缓存。

【问题讨论】:

    标签: javascript android caching webview


    【解决方案1】:

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

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

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

    这里有一些代码sn-ps:

    [资产/js/script.js]:

        'use strict';
    
        function test() {
           // ... do something
        }
    
        // more Javascript
    

    [MainActivity.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");
    
        ...
    

    【讨论】:

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

    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"}
                    }
                });
    

    【讨论】:

    • 什么是clientLogin?啊哈...网络视图
    • 这需要 minSdkVersion 19 或更高版本。
    【解决方案3】:

    是的,您可以使用 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);
           }
        }
    });
    

    【讨论】:

    • 这是一个更好的解决方案,因为您可以更一致地运行 Javascript。
    • 请注意,shouldInterceptRequest(WebView, String) 已被shouldInterceptRequest(WebView, WebResourceRequest) 取代。这个解决方案仍然有效(除了你必须检查request.getUrl() 并记住它的类型是Uri)。另请注意,如果您在没有基本 URL 的情况下使用 WebView.loadData(),则不能在 &lt;script src="..."&gt; 中使用相对路径,似乎 WebView 会忽略它们。请改用loadDataWithBaseURL()
    【解决方案4】:

    使用evaluateJavascript 时要小心:如果您的javascript 中出现语法错误或异常,它会调用您的onReceiveValue 并返回一个空值。支持 SDK 19 和更低版本的最常见方法似乎是这样的:Fill form in WebView with Javascript

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

    另外请注意,使用默认 WebView 您不能使用 javascript 警报来测试任何内容,它们不会显示。另请注意,默认情况下的“视频”(如 html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多