Веб-представление Android: Внесите локальный файл Javascript на удаленную веб-страницу

Это было задано много раз, прежде чем я просмотрел все, пока не получили четких ответов.

Вопрос упрощен: возможно ли добавить локальный файл Javascript (из актива или хранилища) на удаленную веб-страницу, загруженную в веб-представление Android? Я знаю, что такие файлы можно вводить на локальные веб-страницы (Атрибуты HTML), загружаемые в веб-представление.

Зачем мне это нужно?: Чтобы ускорить просмотр, избегайте загрузки больших файлов, таких как Js и CSS файлы каждый раз. Я хочу избежать кэширования веб-представления.

Ответ 1

Существует способ "принудительно" ввести ваши локальные файлы Javascript из локальных активов (например, assets/js/ script.js) и обойти "Не разрешено загружать локальный ресурс: file:///android_assets/js/script.js...".

Это похоже на то, что описано в другом потоке (Android-просмотр Android, загрузка файла javascript в папке с атрибутами), с дополнительным кодированием/декодированием BASE64 для представления вашего файла Javascript как строка для печати. ​​

Я использую Android 4.4.2, API уровня 19 Virtual Device.

Вот несколько фрагментов кода:

[активы/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");

    ...

Ответ 2

Да, вы можете использовать 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);
       }
    }
});

Ответ 3

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

Ответ 4

Будьте осторожны с использованием evaluateJavascript: если в вашем javascript есть синтаксическая ошибка или исключение, она назовет ваш onReceiveValue нулевым значением. Наиболее распространенный способ поддержки как SDK 19, так и ниже выглядит следующим образом: Заполнить форму в WebView с помощью Javascript

Также, если вы ужасно отчаянно нуждаетесь в каких-либо функциях браузера (в моем случае никогда не могли бы понять, как заставить DRM работать хорошо), вы можете использовать букмарклет в обычном хром, который работает только при вводе имени закладки в omnibox, но работает и делает javascript.

Также имейте в виду, что по умолчанию WebView вы не можете использовать предупреждения javascript для тестирования чего-либо, они не отображаются. Также имейте в виду, что по умолчанию "видео" (например, теги html <video> ) не работает "по-настоящему", а также видео по DRM не работает по умолчанию, все параметры настройки:\