1. 程式人生 > >關於android webview讀取js全域性變數或者函式返回值

關於android webview讀取js全域性變數或者函式返回值

背景:藉助現有介面技術,js可以執行原生java程式碼中的方法,可以得到方法的返回值,可以讓原生java程式碼在主執行緒中動態的操作UI;但是藉助該介面,原生java程式碼,採用webview.loadUrl("javascript: JsFunctionName"),只能做到執行js中的方法,如果想獲取js中定義的全域性變數,或者獲取某個js函式的返回值,這種方式無法做到,webview也沒有提供別的函式來可供使用。

分析:為了實現該功能,我們分析application framework的原始碼發現,從webviewloadurl()方法一路追蹤,最終在WebViewCore.java

中找到如下程式碼:

private native voidpassToJs(int frame, int node, int x, int y, int gen,

String currentText, int keyCode, intkeyValue, boolean down,

boolean cap, boolean fn, boolean sym);

BrowserFrame中,追蹤到:

private native voidnativeAddJavascriptInterface(int nativeFramePointer,

Object obj, String interfaceName);

至此我們知道androidwebview實現,使用的是開源的webkit瀏覽器核心,該核心是用c語言(webcore)c++語言(jscore)實現的,androidwebview底層實現最終是呼叫的webkit核心程式碼,如果該核心提供了直接讀取js全域性變數或者函式返回值的方法,那麼我們可以使用JNI(JavaNative Interface)的方式來讀取出來。

方案:

1)反射讀取方式

android.webkit包中有個BrowserFrame私有類,該類中有個Native方法:

public native StringstringByEvaluatingJavaScriptFromString(String script);

這個和蘋果中的類似:

Public NSStringstringByEvaluatingJavaScriptFromString(NSString script);

雖然該類是私有的,但是我們可以利用反射技術來執行這個方法,從而取得js全域性變數和函式返回值;

步驟:

1擴充套件WebView,派生出MyWebView類,新增

publicString stringByEvaluatingJavaScriptFromString(Stringscript)方法,該方法體中最終利用反射技術實現;

2修改佈局中的WebViewcom.appeon.test.MyWebView型別;

3在頁面load完成的情況下,編碼取得JS變數或函式返回值;

2JNI讀取方式

除了採用反射方式能訪問到私有類BrowserFrame中的stringByEvaluatingJavaScriptFromString方法之外,採用JNI技術,也能做到;下面我們採用JNI技術來實現20.4.1中的MyWebView類。

原理:java->C->java,具體到這裡就是mywebview.java呼叫bridge.cbridge.c再呼叫BrowserFrame.java

3)擴充套件webkit方式

直接擴充套件WebCore,擴充套件JSBridge,實現JS的資料型別到JAVA資料型別的轉換,確切的說是相互轉換,這裡面JAVA部分可以用反射機制來做到。

4Plug方式

採用外掛的方式實現。

5)使用android呼叫js後,通過js呼叫android本地方法,從而傳入值的方式。
程式碼如下:
webView.loadUrl("javascript:androidGetInfo()");//android呼叫當前頁面的androidGetInfo()方法。
頁面的js

<script>

      var title = '${article.title}';

      var articleId = '${article.id}';

      var articleType = '${article.type}';

      function androidGetInfo(){

          window.tlsj.getInfo(title,articleType);//呼叫android 中的getInfo方法。

      }

</script>

android手機端程式碼如下:

webView.addJavascriptInterface(newObject() {

          public void getInfo(String _title,String _articleType) {

             title = _title;

             articleType = _articleType;

             Message msg = new Message();

             handler.sendMessage(msg);

          }

      }, "tlsj");
由此即可將js中的全域性變數或者函式返回值傳入android手機端

分析:

反射讀取方式存在一定的不穩定性,如當內部不開放函式或變數名發生變化時,反射就會出現問題。

JNI讀取方式需要程式設計師對c++有一定了解。

使用android呼叫js後,通過js呼叫android本地方法不夠靈活。