1瞭郑、前言
App里基本都少不了H5頁面氮趋,因此JS與Native之間的通信不可避免伍派,最近留意了一些方案,做下總結(jié)剩胁。
2诉植、iOS與H5通信
iOS有兩種webview,ios8以上推出了WKWebView昵观,低于ios8用的是UIWebView晾腔,WKWebView性能上優(yōu)于UIWebView
2.1 舌稀、iOS調(diào)用H5
Native調(diào)用Javascript語言,是通過UIWebView組件的stringByEvaluatingJavaScriptFromString方法來實現(xiàn)的灼擂,該方法返回js腳本的執(zhí)行結(jié)果壁查。
// Swift
webview.stringByEvaluatingJavaScriptFromString("Math.random()")
// OC
[webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];
雙方只需要約定好JS端函數(shù)名稱及參數(shù)
2.2、 H5調(diào)用iOS
JS調(diào)用Native剔应,并沒有現(xiàn)成的API可以使用睡腿,需要借助iframe來實現(xiàn)。原理是在UIWebView內(nèi)發(fā)起的所有網(wǎng)絡(luò)請求峻贮,都可以通過delegate函數(shù)在Native層得到通知席怪。所以只需要劫持該UIWebView內(nèi)的所有請求(通常是這樣的格式:jsbridge://methodName?param1=value1¶m2=value2),然后在UIWebView的delegate函數(shù)中纤控,只要發(fā)現(xiàn)是jsbridge://開頭的地址挂捻,就不進行內(nèi)容的加載,轉(zhuǎn)而執(zhí)行相應(yīng)的調(diào)用邏輯:
// JS 端關(guān)鍵代碼
var url = 'jsbridge://doAction?title=分享標(biāo)題&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端關(guān)鍵代碼
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調(diào)用H5
在android里是使用webview的loadUrl進行調(diào)用的
// 調(diào)用js中的JSBridge.trigger方法
webView.loadUrl("javascript:JSBridge.trigger('webviewReady')");
3.2、 H5調(diào)用Android
有兩種比較好的方式:
- 和iOS一樣唬涧,通過iframe(Android端通過shouldOverrideUrlLoading方法對url協(xié)議進行解析)
- 通過在webview頁面里直接注入原生js代碼方式疫赎,使用addJavascriptInterface方法來實現(xiàn)盛撑。
在android里實現(xiàn)如下:
class JSInterface {
@JavascriptInterface //注意這個代碼一定要加上
public String getUserData() {
return "UserData";
}
}
webView.addJavascriptInterface(new JSInterface(), "AndroidJS"); //window對象里注入了AndroidJS對象
JS端可以直接調(diào)用:alert(AndroidJS.getUserData()) //UserDate
4碎节、iOS與H5通信的其它方案
基于 callHandler 和 registerHandler的方式,比較干凈
- JS調(diào)用Native
setupWebViewJavascriptBridge(e => {
e.callHandler("getHttpHeader", {}, function(data) { //getHttpHeader方法在ios端定義好
fn(data);
})
})
- Native調(diào)用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抵卫、小結(jié)
- iOS調(diào)H5(stringByEvaluatingJavaScriptFromString)狮荔,H5調(diào)iOS(iframe,schema協(xié)議)
- Android調(diào)H5(webView.loadUrl()),H5調(diào)Android(1介粘、iframe殖氏;2、addJavascriptInterface)
- 從可維護性上看姻采,H5端都用iframe方式調(diào)用iOS&Android最好
- 從實際操作上看雅采,H5端需要維護一個專門用于和Native端通信的js庫(封裝iframe及一些方法定義),俗稱SDK慨亲;
Native端需要各自與H5端定義的函數(shù)對接婚瓜。
6、參考文檔
1刑棵、Web 與 App 數(shù)據(jù)交互原理和實現(xiàn)
2巴刻、WK 與 JS 的那些事
3、H5 與 Native 交互之 JSBridge 技術(shù)
4蛉签、WebView 開車指南之最全實用案例