android_js
前言
作為開發人員都知道,客戶端的版本更新對於使用者來說代價是很大的。為了滿足客戶端能夠快速更新迭代的要求,許多app都內嵌入了H5,比如很多電商平臺,淘寶、京東、
聚划算等等。這類技術的關鍵就是在於Android客戶與Web前端之間的互動。很多大型專案的介面為了防止Spammer的侵入,都是要求只能由客戶端發起請求的。所以本專案
就封裝了一個module,實現客戶端接收前端的呼叫,然後由客戶端發起Http請求的功能。
複製程式碼
開始介紹專案之前,先來快速回顧一下Android客戶端與Web前端之間互動的幾種方式。
複製程式碼
1. Android呼叫JS方法
1.1 通過WebView的loadUrl()
android客戶端程式碼:
private void initWebView() {
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true); // 設定與Js互動的許可權
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 設定允許JS彈窗
webView.loadUrl("file:///android_asset/javascript.html");
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(SimpleWebViewActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
});
}
private void setListener() {
btnLoadUrl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
webView.post(new Runnable() {
@Override
public void run() {
// 此處的callJS方法名與JS中的function方法名必須要一致
webView.loadUrl("javascript:callJS()");
}
});
}
});
}
複製程式碼
javascript前端程式碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
// JS程式碼
<script>
// Android需要呼叫的方法
function callJS() {
alert("Android呼叫了JS的callJS方法");
}
</script>
</head>
</html>
複製程式碼
執行結果如圖
複製程式碼
android客戶端程式碼:
btnEvaluateJavascript.setOnClickListener(new View.OnClickListener() {
@TargetApi(19)
@Override
public void onClick(final View v) {
webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處為 js 返回的結果
Log.d(TAG, "value---" + value);
}
});
}
});
複製程式碼
執行結果跟上圖是一樣的。
複製程式碼
兩種互動方式的比較
複製程式碼
呼叫方式 | 優點 | 缺點 | 使用場景 |
---|---|---|---|
使用loadUrl() | 方便簡潔 | 效率低;獲取返回值麻煩 | 不需要使用返回值,對效能要求較低 |
使用evaluateJavascript() | 效率高 | 向下相容性差(僅Android 4.4以上可用) | Android 4.4及以上 |
1.3 使用建議
// Android版本變數
final int version = Build.VERSION.SDK_INT;
// 因為該方法在 Android 4.4 版本才可使用,所以使用時需進行版本判斷
if (version < 18) {
webView.loadUrl("javascript:callJS()");
} else {
webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處為 js 返回的結果
}
});
}
複製程式碼
2. JS呼叫Android方法
2.1 通過WebView的addJavascriptInterface()進行物件對映
程式碼不再貼出,詳細程式碼請參見:程式碼地址
執行結果如下:
2.2 通過 WebViewClient 的方法shouldOverrideUrlLoading()回撥攔截 url
程式碼不再貼出,詳細程式碼請參見:程式碼地址
執行結果如下:
2.3 通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回撥攔截JS對話方塊alert()、confirm()、prompt()訊息
程式碼不再貼出,詳細程式碼請參見:程式碼地址
執行結果如下:
2.3.1 onJsAlert()、onJsConfirm()、onJsPrompt()三者之間的比較
方法 | 作用 | 返回值 |
---|---|---|
alert() | 彈出警告框 | 沒有 |
confirm() | 彈出確認框 | 兩個返回值(true或false) |
prompt() | 彈出輸入框 | 任意設定返回值 |
2.3.2 總結
常用的攔截是:攔截 JS的輸入框(即prompt()方法),因為只有prompt()可以返回任意型別的值,操作最全面方便、更加靈活,
而alert()對話方塊沒有返回值,confirm()對話方塊只能返回兩種狀態(確定 / 取消)兩個值。
複製程式碼
2.4 三種Android Call Js 方式的對比以及使用場景
呼叫方式 | 優點 | 缺點 | 使用場景 |
---|---|---|---|
通過WebView的addJavascriptInterface() 進行物件對映 |
方便簡潔 | Android 4.2以下存在漏洞問題 | Android 4.2以上相對簡單的互調場景 |
通過 WebViewClient 的方法shouldOverrideUrlLoading() 回撥攔截 url |
不存在漏洞問題 | 有協議約束,客戶端向前端傳值繁瑣 | 不需要返回值的互調場景 |
通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法 回撥攔截JS對話方塊alert()、confirm()、prompt()訊息 |
不存在漏洞問題 | 有協議約束 | 能滿足大多數情況下的互調場景 |
附:WebView的addJavascriptInterface()方法在Android 4.2以下存在的漏洞
以上都是對基礎知識的回顧,下面的才是本專案的解釋說明。注意了,以下才是本專案的解釋說明!!
先來看一下專案執行的效果圖:
複製程式碼
大家會說,這個不是跟攔截JS的prompt()方法一樣麼,沒錯,js和android之間的互動,也無非上面提到的幾種方法,這裡做的封裝採用的是:
js調android:通過WebView的addJavascriptInterface()進行物件對映
android回撥js:android 4.4以上採用WebView的evaluateJavascript()方法,android 4.4以下采用loadUrl()方法
複製程式碼
android端程式碼:
複製程式碼
JsBridgeWebView 這是一個繼承WebView的類,它裡面向JS注入了一個物件供JS呼叫,JS可以通過這個物件呼叫Native的方法,調哪個方法,傳哪些引數,完全由JS決定,方法名必須兩端協議,Native通過反射找到對應的方法。傳遞過來的引數重包含了JS回撥方法的方法名,客戶端執行完相應的操作之後再去執行JS的方法。
js程式碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
// 由於物件對映,所以呼叫jsCallback物件等於呼叫Android對映的物件
var json = "{"name": "zlove", "_dscbstub": "callback"}"
_jsbridge.call("testAsync", json);
}
function callback(result) {
alert("客戶端返回的結果是:" + result)
}
</script>
</head>
<body>
<!-- 點選按鈕則呼叫callAndroid函式 -->
<button type="button" id="button1" style="font-size:30px" onclick="callAndroid()">Call Android</button>
</body>
</html>
複製程式碼
var json = "{"name": "zlove", "_dscbstub": "callback"}"
_jsbridge.call("testAsync", json);
_jsbridge表示客戶端注入的物件,testAsync是方法名,json是引數,而_dscbstub對應的callback是js的回撥方法。
複製程式碼
end
本來以為要寫很多,事實上其實把基礎原理寫清楚了,也就這麼多???。
I hope this will help you!
複製程式碼
附:原始碼地址