JSBridge 其实在职业生涯中已经多次用过了,例如微信的 WeiXinJSBridge(JSSDK)、RN 的 window.postMessage,但让我自己描述这一过程所用的语句还非常“外行”,现在系统整理一下。
原文地址:https://juejin.im/post/5abca877f265da238155b6bc
原理
- JSBridge 调用可以看作一次 RPC(remote procedure call,远程过程调用)。前端是 RPC 的客户端,Native 是 RPC 的服务端
- 简单来说就是各种 Web 页面可以通过 Bridge 来调用应用的一些原生功能。主要就是给 JS 提供能调用 Native 功能的接口,让前端部分可以使用地理位置、摄像头或者支付等 Native 功能
双向通信
- JS 向 Native 发送调用指令,通知 Native 当前 JS 状态
- Native 向 JS 回溯调用结果(callback),消息推送,通知 JS 当前 Native 状态
JSBridge 调用
JS 调用 Native
注入 API
安卓和 iOS 编写各自的代码,将 api 注入到 webview 的 window 下面,前端进行调用。
1
window.webkit.messageHandlers.nativeBridge.postMessage(message);
拦截 URL SCHEME
前端直接 iframe.src
或 location.href
,需要提前跟 Native 进行协商。但存在两个问题:
- url 长度缺陷
- location.href 连续调用 Native 会导致一些调用丢失
Native 调用 JS
Native 直接执行 JS 代码,因此方法必须挂在 window 下面,否则 Native 没办法拿到方法
JSBridge 实现
JSBridge 接口主要功能有两个
- 给 Native 发消息
- 被 Native 调用
同时要实现给 Native 发消息后的【回调】,大概的 window.JSBridge 长这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
(function () {
var id = 0,
callbacks = {},
registerFuncs = {};
window.JSBridge = {
// 调用 Native
invoke: function (bridgeName, callback, data) {
// 判断环境,获取不同的 nativeBridge
var thisId = id++; // 获取唯一 id
callbacks[thisId] = callback; // 存储 Callback
nativeBridge.postMessage({
bridgeName: bridgeName,
data: data || {},
callbackId: thisId, // 传到 Native 端
});
},
receiveMessage: function (msg) {
var bridgeName = msg.bridgeName,
data = msg.data || {},
callbackId = msg.callbackId, // Native 将 callbackId 原封不动传回
responstId = msg.responstId;
// 具体逻辑
// bridgeName 和 callbackId 不会同时存在
if (callbackId) {
if (callbacks[callbackId]) {
// 找到相应句柄
callbacks[callbackId](msg.data); // 执行调用
}
} else if (bridgeName) {
if (registerFuncs[bridgeName]) {
// 通过 bridgeName 找到句柄
var ret = {},
flag = false;
registerFuncs[bridgeName].forEach((callback) => {
callback(data, function (r) {
flag = true;
ret = Object.assign(ret, r);
});
});
if (flag) {
nativeBridge.postMessage({
// 回调 Native
responstId: responstId,
ret: ret,
});
}
}
}
},
register: function (bridgeName, callback) {
if (!registerFuncs[bridgeName]) {
registerFuncs[bridgeName] = [];
}
registerFuncs[bridgeName].push(callback); // 存储回调
},
};
})();