jsBridge在支付,钱包,媒体拓展,图片处理,活动页面,用户地理位置网络状态都能得到原生强有力支持;
前端和Native对对方的细节知道的越少越好,减少耦合度,暴露的接口尽量控制在5个以内;
实现方式(交互形式)
Native 调用 JS
使用前端暴露在window下的一个方法或者一个对象的方法;
window._handlerFromApp(message)
window.JSBridge._handlerFromApp(message)
message: {
cbId : "cb_(:id)_(:timeStamp)", //回调函数的id
status: 0, //状态数据 (0:失败, 1:成功)
msg : "ok", //反馈的消息
data : {
//... //一些处理后的数据
}
}
JS调用Native
以下只介绍前两个方法,第三个和第二个比较类似
- A. Native暴露一个含有通信方法的类给web调用
- B. Native拦截iframe请求
- C. Native拦截prompt弹出框
A 一个包含调用方法的类
iOS : 可使用javascriptCore
Android: 直接使用WebView的addJavascriptInterface方法
将一个js对象绑定到一个Native类,在类中实现相应的函数,当js需要调用Native的方法时,只需要直接在js中通过绑定的对象调用相应的函数
确定对象名称: (:AppName)JSBridge
Native提供的对象含有的方法:
invoke(funcName, data)
listen(funcName, data)
invoke
:用于web页面调用Native私有方法的通用方法
参数: funcName
, data
funcName
:对应为Native内部私有方法的方法名或映射data
:web传递给Native的必要数据
// data 数据
{
cbId : "cb_(:id)_(:timeStamp)", //回调函数的id
msg : {} //提供给使用方法执行的一些参数
}
/**
//1.拿wx参考为例
wx.previewImg({
current: 'http://xxx_1.png',
urls : [
'http: //xxx_0.png',
'http: //xxx_1.png',
'http: //xxx_2.png',
'http: //xxx_3.png',
]
});
//2.因为wx对jsbridge进行了一次封装,jssdk, 而我们在未封装时应该如下使用
JSBridge.invoke('imagePreview', {
cbId : "cb_(:id)_(:timeStamp)",
msg : {
current: 'http://xxx_1.png',
urls : [
'http: //xxx_0.png',
'http: //xxx_1.png',
'http: //xxx_2.png',
'http: //xxx_3.png',
]
}
});
那么当调用之后,Native执行完成对应的私有方法后,执行一次我们提供的回调接口,以下是javascript的语法,请Native开发工程师对应修改
JSBridge.handlerFromApp({
cbId : "cb_(:id)_(:timeStamp)", //web传给Native的cbId
status: 1, //状态数据 (0:失败, 1:成功)
msg : "预览成功",
data : {}
});
listen
是一个用于web页面监听Native方法实现的通用方法
使用环境: 不属于web页面上的操作。当用户直接操作Native上的功能来影响或发送数据给web,或者操作的功能需要用到web页面上的数据,我们需要告知Native我们希望能收到回调;
例子: 微信监听分享操作
- 分享的内容是web上的内容(标题,描述,图片);
- 获取分享操作是否完成和分享操作的数据收集;
- 分享按钮是原生APP提供;
数据结构和操作与invoke
相似,对应Native开发哥们接收到listen操作后需要存储一个映射,在被监听的操作实现上判断是不是需要执行web端提供的回调接口;
B iframe的魔法
由于Native App可以监听webview的请求,所以web端通过创建一个隐藏的iframe,请求商定后的统一协议来发送数据给Native App;
需要Native开发兄弟在webview开启时候为页面注入jsbridge.js代码并执行(防止被前端浏览器直接查看源代码了解app的代码逻辑)
页面前期准备
1.app打开webview
2.loadUrl(页面url)
3.监听webview开始,并执行一段js代码将包内的jsbridge.js文件引入页面中;
比较
A:android曝安全漏洞,但相对来说实现简单,调用方式容易,且传递参数,无需前端搭建jsbridge,只需要封装易用的sdk,App不需要读取本地静态js文件;
B: iframe规定协议,规范统一,需要前端实现jsbridge和封装sdk, iframe通过url的方式,数据统一为字符串格式,数据量受限制,两端要转义字符;
C: prompt在一些安卓设备受系统劫持,监听prompt兼容性需要测试,也是字符串形式,数据量不受限,需要转义字符;
/**
* 用法:
* import jsBridge from 'fileName.js'
*
* 1、给 APP 端发送数据
* jsBridge.callHandler(eventName, data, callback(reponseData))
* 参数说明:
* eventName (string): 必传, 与 APP 端约定的事件名
* data (object): 非必传, 发送给 APP 端的数据
* callback (function): 通信完成后,前端的回调,reponseData,是APP端返回的数据
*
* 2、接收 APP 端的数据
* jsBridge.registerHandler(eventName, callback(data, responseCallback))
* 参数说明:
* eventName (string): 必传,与 APP 端约定的事件名
* callback (function): data: 是接收到的数据,responseCallback,通信完成后,传给 APP 端的回调
*/
const isAndroid = navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Adr') > -1;
const isiOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
//这是必须要写的,用来创建一些设置
function setupWebViewJavascriptBridge(callback) {
if (isAndroid) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady',
function () {
callback(WebViewJavascriptBridge)
},
false
);
}
}
if (isiOS) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
let WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function () {
document.documentElement.removeChild(WVJBIframe)
}, 0);
}
}
setupWebViewJavascriptBridge(function (bridge) {
if (isAndroid) {
//安卓端,接收数据时,需要先进行初始化
bridge.init(function (message, responseCallback) {
const data = {
'Javascript Responds': 'Wee!'
};
responseCallback(data);
})
}
})
export default {
// 给APP发送数据
callHandler(name, data, callback) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.callHandler(name, data, callback)
})
},
// 接收APP端的数据
registerHandler(name, callback) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.registerHandler(name, function (data, responseCallback) {
callback(data, responseCallback)
})
})
}
}