Android中webview填坑系列——向webview注入本地js文件

需求:

    在使用webview加載H5界面時,注入一個本地js文件(該js文件的作用是採集H5頁面中用戶的點擊事件)。也就是在webview打開了一個H5頁面的時候動態的注入一個js,讓該js和頁面並行執行各自的邏輯。

思路:

    查閱了一下網上的資料,思路也就兩種,這裏先列出來,後面再對這兩種思路做評判,如下所示:

(1)第一種思路:將本地的js文件讀出來已字符串的形式,通過webview.loadUrl("javascript:",""); 注入進入,注入的時機是在onPageFinished方法中注入。

String jsStr = "";
    try {
      InputStream in = DefaultDCollectionClient.mContext.getAssets().open("jsStr.js");
      byte buff[] = new byte[1024];
      ByteArrayOutputStream fromFile = new ByteArrayOutputStream();
      do {
        int numRead = in.read(buff);
        if (numRead <= 0) {
          break;
        }
        fromFile.write(buff, 0, numRead);
      } while (true);
      jsStr = fromFile.toString();
      in.close();
      fromFile.close();
    } catch (IOException e) {
      e.printStackTrace();
    }

onPageFinished方法中執行如下代碼:

@Override
public void onPageFinished(WebView view, String url) 
 {
        super.onPageFinished(view, url);
         webview.loadUrl("javascript:" + jsStr );
 }

(2)第二種思路:是將本地assets中的js文件以script標籤的形式直接注入到html中。

String js = "var newscript = document.createElement(\"script\");";
       js += "newscript.src=\"file:///android_asset/jsStr.js\";";
       js += "document.body.appendChild(newscript);";

onPageFinished方法中執行

@Override
 public void onPageFinished(WebView view, String url) 
  {
       super.onPageFinished(view, url);
        webview.loadUrl("javascript:" + js);
  }

本地assets中的 js 文件代碼比較長我就只放個提綱在這裏。

(function(){

 var device = Device = (function () { ...})();
 function inject(json){...}
 inject("log-test-begin");
 function getInfo(element){...}
 function getChain(element){...}
 function addObserver(element, event, func, capture){...}
 inject("log-test-before");
 window.onload = function(){...}
}()) 

---------------------------------華麗的分割線----------------------------

上面兩種思路基本就是網上能搜到的大多數文章的思路,本人親測對於我目前的需求這兩種方式都無效,並且假如webview或者H5設置了js防注入的話,第二種方式直接就炸了,直接無效。於是乎咋辦呢???  當前是看webview的源碼。。。

解決方案:

通過看webview的源碼發現還有一個方法比較有意思,就是下面這個方法。

 /**
     * Asynchronously evaluates JavaScript in the context of the currently displayed page.
     * If non-null, |resultCallback| will be invoked with any result returned from that
     * execution. This method must be called on the UI thread and the callback will
     * be made on the UI thread.
     * <p>
     * Compatibility note. Applications targeting {@link android.os.Build.VERSION_CODES#N} or
     * later, JavaScript state from an empty WebView is no longer persisted across navigations like
     * {@link #loadUrl(String)}. For example, global variables and functions defined before calling
     * {@link #loadUrl(String)} will not exist in the loaded page. Applications should use
     * {@link #addJavascriptInterface} instead to persist JavaScript objects across navigations.
     *
     * @param script the JavaScript to execute.
     * @param resultCallback A callback to be invoked when the script execution
     *                       completes with the result of the execution (if any).
     *                       May be null if no notificaion of the result is required.
     */
    public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
        checkThread();
        mProvider.evaluateJavaScript(script, resultCallback);
    }

他是可以在UI線程執行的異步加載js的方法,於是乎就用它來試一下,代碼如下,

//    1) 從文件中讀取js爲字符串

 String jsStr = "";
    try {
      InputStream in = DefaultDCollectionClient.mContext.getAssets().open("rattrap.js");
      byte buff[] = new byte[1024];
      ByteArrayOutputStream fromFile = new ByteArrayOutputStream();
      do {
        int numRead = in.read(buff);
        if (numRead <= 0) {
          break;
        }
        fromFile.write(buff, 0, numRead);
      } while (true);
      jsStr = fromFile.toString();
      in.close();
      fromFile.close();
    } catch (IOException e) {
      e.printStackTrace();
    }

//    2) 將讀取的js字符串注入webview中

 @Override public void onPageStarted (WebView view, String url, Bitmap favicon){
        if (Build.VERSION.SDK_INT >= 19) {
          webView.evaluateJavascript(jsStr, new ValueCallback<String>() {
            @Override public void onReceiveValue(String value) {//js與native交互的回調函數
              Logs.d(TAG, "value=" + value);
            }
          });
        }
      }

或許細心的會發現我注入的時機是在 onPageStarted 方法中 ,由於 webView.evaluateJavascript 是異步操作,所以需要在onPageStarted 中注入,因爲webview執行onPageFinished 方法時表示頁面已經加載完成,開始執行js,那麼在onPageFinished中注入的話,等我們的js注入時,H5頁面的js基本執行完畢,我們注入的js就不會生效。當然別忘了添加webView.getSettings().setJavaScriptEnabled(true); 這樣我們的js就已字符串的形式注入到當前H5界面中了,當用戶在H5界面中做操作的話,我們就可以在注入的js中採集相關的數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章