1、前言
App里基本都少不了H5頁面,因此JS與Native之間的通信不可避免,最近留意了一些方案,做下總結。
2、iOS與H5通信
iOS有兩種webview,ios8以上推出了WKWebView,低于ios8用的是UIWebView,WKWebView性能上優于UIWebView
2.1 、iOS調用H5
Native調用Javascript語言,是通過UIWebView組件的stringByEvaluatingJavaScriptFromString方法來實現的,該方法返回js腳本的執行結果。
// Swift
webview.stringByEvaluatingJavaScriptFromString("Math.random()")
// OC
[webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];
雙方只需要約定好JS端函數名稱及參數
2.2、 H5調用iOS
JS調用Native,并沒有現成的API可以使用,需要借助iframe來實現。原理是在UIWebView內發起的所有網絡請求,都可以通過delegate函數在Native層得到通知。所以只需要劫持該UIWebView內的所有請求(通常是這樣的格式:jsbridge://methodName?param1=value1¶m2=value2),然后在UIWebView的delegate函數中,只要發現是jsbridge://開頭的地址,就不進行內容的加載,轉而執行相應的調用邏輯:
// JS 端關鍵代碼
var url = 'jsbridge://doAction?title=分享標題&desc=分享描述&link=http%3A%2F%2Fwww.baidu.com';
var iframe = document.createElement('iframe');
iframe.style.width = '1px';
iframe.style.height = '1px';
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
setTimeout(function() {
iframe.remove();
}, 100);
// OC端關鍵代碼
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
print("shouldStartLoadWithRequest")
let url = request.URL
let scheme = url?.scheme
let method = url?.host
let query = url?.query
if url != nil && scheme == "jsbridge" {
print("scheme == \(scheme)")
print("method == \(method)")
print("query == \(query)")
switch method! {
case "getData":
self.getData()
case "putData":
self.putData()
case "gotoWebview":
self.gotoWebview()
case "gotoNative":
self.gotoNative()
case "doAction":
self.doAction()
case "configNative":
self.configNative()
default:
print("default")
}
return false
} else {
return true
}
}
3、Android與H5通信
3.1 、Android調用H5
在android里是使用webview的loadUrl進行調用的
// 調用js中的JSBridge.trigger方法
webView.loadUrl("javascript:JSBridge.trigger('webviewReady')");
3.2、 H5調用Android
有兩種比較好的方式:
- 和iOS一樣,通過iframe(Android端通過shouldOverrideUrlLoading方法對url協議進行解析)
- 通過在webview頁面里直接注入原生js代碼方式,使用addJavascriptInterface方法來實現。
在android里實現如下:
class JSInterface {
@JavascriptInterface //注意這個代碼一定要加上
public String getUserData() {
return "UserData";
}
}
webView.addJavascriptInterface(new JSInterface(), "AndroidJS"); //window對象里注入了AndroidJS對象
JS端可以直接調用:alert(AndroidJS.getUserData()) //UserDate
4、iOS與H5通信的其它方案
基于 callHandler 和 registerHandler的方式,比較干凈
- JS調用Native
setupWebViewJavascriptBridge(e => {
e.callHandler("getHttpHeader", {}, function(data) { //getHttpHeader方法在ios端定義好
fn(data);
})
})
- Native調用JS
setupWebViewJavascriptBridge(e => {
e.registerHandler("navBarButtonClicked", (data, responseCallback) => { //H5端注冊navBarButtonClicked
if (data == 'mine') {
...
}
})
})
//
const setupWebViewJavascriptBridge = e => {
if (window.WebViewJavascriptBridge)
return e(WebViewJavascriptBridge);
if (window.WVJBCallbacks)
return window.WVJBCallbacks.push(e);
window.WVJBCallbacks = [e];
var t = document.createElement("iframe");
t.style.display = "none";
t.src = WVJBIframeSrc();
document.documentElement.appendChild(t);
setTimeout(function() {
document.documentElement.removeChild(t)
}, 0)
};
5、小結
- iOS調H5(stringByEvaluatingJavaScriptFromString),H5調iOS(iframe,schema協議)
- Android調H5(webView.loadUrl()),H5調Android(1、iframe;2、addJavascriptInterface)
- 從可維護性上看,H5端都用iframe方式調用iOS&Android最好
- 從實際操作上看,H5端需要維護一個專門用于和Native端通信的js庫(封裝iframe及一些方法定義),俗稱SDK;
Native端需要各自與H5端定義的函數對接。
6、參考文檔
1、Web 與 App 數據交互原理和實現
2、WK 與 JS 的那些事
3、H5 與 Native 交互之 JSBridge 技術
4、WebView 開車指南之最全實用案例