關於android webview讀取js全域性變數或者函式返回值
背景:藉助現有介面技術,js可以執行原生java程式碼中的方法,可以得到方法的返回值,可以讓原生java程式碼在主執行緒中動態的操作UI;但是藉助該介面,原生java程式碼,採用webview.loadUrl("javascript: JsFunctionName"),只能做到執行js中的方法,如果想獲取js中定義的全域性變數,或者獲取某個js函式的返回值,這種方式無法做到,webview也沒有提供別的函式來可供使用。
分析:為了實現該功能,我們分析application framework的原始碼發現,從webview類loadurl()方法一路追蹤,最終在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);
至此我們知道android的webview實現,使用的是開源的webkit瀏覽器核心,該核心是用c語言(webcore)和c++語言(jscore)實現的,android的webview底層實現最終是呼叫的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、修改佈局中的WebView為com.appeon.test.MyWebView型別;
3、在頁面load完成的情況下,編碼取得JS變數或函式返回值;
(2)JNI讀取方式
除了採用反射方式能訪問到私有類BrowserFrame中的stringByEvaluatingJavaScriptFromString方法之外,採用JNI技術,也能做到;下面我們採用JNI技術來實現20.4.1中的MyWebView類。
原理:java->C->java,具體到這裡就是mywebview.java呼叫bridge.c,bridge.c再呼叫BrowserFrame.java
(3)擴充套件webkit方式
直接擴充套件WebCore,擴充套件JSBridge,實現JS的資料型別到JAVA資料型別的轉換,確切的說是相互轉換,這裡面JAVA部分可以用反射機制來做到。
(4)Plug方式
採用外掛的方式實現。
(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本地方法不夠靈活。