H5 与Native的交互方案

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&param2=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 开车指南之最全实用案例

推荐阅读更多精彩内容