H5与原生交互

 H5与原生应用的交互都是通过原生应用中的WebView实现的。通过这个环境,H5可以调用原生应用注入其中的原生对象的方法,原生应用也可以调用H5暴露在这个环境中的JavaScript对象的方法,从而实现指令与数据的传输。

原生应用和JS分别在WebView里注入/暴露的对象:

  • NativeBridge:原生应用注入到WebView中的对象

  • JSBridge:JS暴露在WebView中的对象

并约定在这两个对象上分别可以调用什么方法:

  • NativeBridge.callNative(action, params, whoCare)

  • JSBridge.callJS(action, params, whoAmI)

NativeBridge.callNative是由JS调用向Native传递指令或数据的方法,而JSBridge.callJS则是由Native调用向JS传递指令或数据的方法。方法签名中的参数含义如下:

  •  action:字符串,希望Native/JS执行的操作

  •  params:JSON对象,要传给Native/JS的数据

  •  whoCare:数值,表示JS希望哪个端响应

  • whoAmI:数值,表示哪个端调用的JS

基础接口只有两个对象和两个方法,JS与App间的互操作则通过action和params来扩展和定义。

由JS发起的单向调用App的操作,如加载URL和切换到原生界面,可对应的action为:loadUrl:加载另一个h5的URL;loadContent:跳转到相应的原生界面。

这里NativeBridge是App的原生对象,其callNative方法被调用时,会收到一个对象(字典/映射)参数。根据这个参数的action属性的值,App可知需要执行的操作是加载URL或跳转到原生界面。

一下代码App可知需要执行的操作是加载URL。于是再取得params属性中的url,发送请求即可:

NativeBridge.callNative({
 action: 'loadUrl',
 params: { url },
 whoCare: 0
})

以下代码通过params向App传递了必要参数,App负责切换到相应的原生界面:

NativeBridge.callNative({
    action: "loadContent",
    params: {
        type: "album",
        content: {
            album_id: "1"
        }
    },
    whoCare: 0
})

由App发起的单向调用JS的操作,如用户点击后退按钮(<)时,可对应的action为:can_back:询问JS是否返回前是否需要用户确认(即在返回上一级界面前,是否弹窗提示用户?)。参考协议如下:

JSBridge.callJS({
 action: "can_back",
 params: {},
 whoAmI: 1/2
})

此调用返回的值示例如下:

{
 can: true,
 target: "prev"
}

返回值中的can如果是true,则不提示直接返回;如果是false,则弹出一个确认框,请用户确认。另一个值target是与App约定的返回目标,比如prev表示返回上一级,top表示返回顶级,等等。

WebViewJavascriptBridge

使用WebViewJavascriptBridge和原生交互跳转

页面代码

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=0.5,maximum-scale=0.5,user-scalable=no" />
        <script src="bridge.js"></script>
    </head>
    <body></body>
    <script>
        let paramsObj = getParamsObj(window.location.search);
         bridge.callHandler('callNative', {a: 1}, (data) => {
             // 回调函数
             console.log(data);
         });
    </script>
</html>

brige.js文件

/**
 * android系统js桥接口设置
 * @param {Object} callback 桥接口回调函数 桥接方式固定写法
 */
function setUpAndroidBridge(callback) {
    if (window.WebViewJavascriptBridge) {
        return callback(WebViewJavascriptBridge);
    } else {
        document.addEventListener(
            'WebViewJavascriptBridgeReady',
            function () {
                return callback(WebViewJavascriptBridge);
            },
            false
        );
    }
}

/**
 * IOS系统js桥接口设置
 * @param {Object} callback 桥接口回调函数 桥接方式固定写法
 
 */
function setUpIOSBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function () { document.documentElement.removeChild(WVJBIframe) }, 0)
}


let bridge = {
    callHandler(name, data, resultCallback) {
        var osType = getOSType();//获取系统类型
        //按系统类型 分别执行原生交互
        if (osType == 'IOS') {
            //苹果手机交互方式 消息体为字面量对象(json格式): action 代表执行的动作 data 代表传递的数据
            setUpIOSBridge(function (bridge) {
                bridge.callHandler(name, data, function (response) {
                    if (typeof resultCallback == 'function') {
                        resultCallback(response);
                    }
                });
            });
        } else if (osType == 'ANDROID') {
            //安卓手机交互方式
            setUpAndroidBridge(function (bridge) {
                bridge.callHandler(name, data, function (response) {
                    if (typeof resultCallback == 'function') {
                        resultCallback(response);
                    }
                });
            });
        } else {
            //其他类型
        }
    }
}

 jsbridge实现原理

js调用Native

通过特定的参数转换方法,将传入的数据,方法名一起,拼接成一个url scheme,以下只介绍前两个方法,第三个和第二个比较类似用于触发这个url scheme:

A. Native暴露一个含有通信方法的类给web调用

B. Native拦截iframe请求

C. Native拦截prompt弹出框

Native调用JS

Native调用Javascript语言,是通过UIWebView组件的stringByEvaluatingJavaScriptFromString方法来实现的,该方法返回js脚本的执行结果。

// Swift
webview.stringByEvaluatingJavaScriptFromString("Math.random()")
// OC
[webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];

从上面代码可以看出它其实就是调用了window下的一个对象,如果我们要让native来调用我们js写的方法,那这个方法就要在window下能访问到。但从全局考虑,我们只要暴露一个对象如JSBridge对native调用就好了

原文:

http://www.fly63.com/article/detial/2824

原文地址:https://www.cnblogs.com/xjy20170907/p/11023783.html