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 开车指南之最全实用案例
网友评论