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调用就好了
原文: