Connecting native and webview
Android 可以直接向 Webview 中注入 JavaScript 对象,但是该对象仅直接纯函数调用,同时在 4.2 版本以下存在安全漏洞。
iOS 则无法注入任何 JavaScript 对象。
于是面向业务需要一套 SDK 能够:
Android 端使用 prompt 方式。这是一个不常用的 BOM 方法,可以被 Native 代码选择性捕获。其唯一参数为字符串类型,用以承载通信数据。
window.prompt('{"message": "I am Yanni4night"}');
iOS 端无法通过调用任何方法来直接与 Native 端通信,但可以通过 Frame 元素的请求来被 Native 端选择性捕获。可以通过 Frame 的 URL 携带通信数据。
var iframe = document.createElement('iframe');
iframe.src = "scheme://data?p=%7B%22message%22%3A%22I%20am%20Yanni4night%22%7D";
为规避 URL 可能存在的长度限制,一般仅利用它通知 Native,再由 Native 来抓取数据。
消息是由消息类型、请求数据、响应数据、callback ID等构成的纯 JavaScript 对象。
{ messageType: inputData: outputData: errNo: errMsg: data: callbackId: }
消息的流转全部通过一个发送队列和一个接收队列。
这里仿照 TCP 的 ACK 包,要求每一个请求消息都必须有 callback 回复确认。
API 的命名空间,Native/Webview 意义相同,不公用。
API 的方法名,Native/Webview 意义相同,不公用。
允许被调用方以异步的方式响应请求,这可能会受到调用方超时时间的限制。
直接源码构建:
$ npm install
$ make
或者通过 NPM:
npm install --save nwbridge
调用 NWBridge 构造函数。
new NWBridge('__tb_js_bridge', 'JsBridge', 'schema://', hybridInitialData)
上面的代码会在全局挂在两个变量,__tb_js_bridge 和 JsBridge,相同变量名重复构造无效。
如果你是通过 NPM 安装,那么你可能需要:
import NWBridge from 'nwbridge'
以下所有 API 都挂在同一变量下,命名允许配置,由 Webview 业务端代码(Js)调用。为简便起见,这里以 JsBridge 为例。注意 JsBridge 对象不一定存在。
该属性指示访问 Native 的能力状态,可能有三个取值:pending、complete、error。只有当该值为 complete 时以下方法才存在。
alert(JsBridge.readyState)
访问 Native 的预定义方法,返回一个 Promise 对象。
JsBridge.call('dialog', 'confirm', {msg: 'R u sure?'}, 3000)
.then(function (yes) {});
注册一个在 Webview 定义的供 Native 调用的 API。
JsBridge.register('location', 'search',
function(){
return location.search;
}, false);
如果是异步的:
JsBridge.register('location', 'search',
function(cb){
cb(location.search);
}, true);
以下所有 API 都挂在同一变量下,命名允许配置,由 Native 代码(Java/Object-C)调用。为简便起见,这里以 __js_bridge 为例。
向 Webview 发送消息,参数必须是合法 JSON 语法的字符串形式,必须符合上面 Message 的格式,如:
__js_bridge.send('{"messageType": "request"}')
iOS 端用来从 Webview 主动抓取数据的方法,不存在于 Android 端。返回值为字符串类型,为合法的 JSON 数组类型,数组中每个成员为上面的 Message 格式。
__js_bridge.fetch()
Native 直接访问 JavaScript,会被 Webcore 的单线程 hang 住,为了不影响客户端主线程的渲染,这里使用 setTimeout 或 setImmediate 将后续逻辑发到下一个执行周期中。
注意这个定时器在某些情况下可能被取消,因此只要没被取消,应尽可能地处理数据,避免“失活”。
使用 document 事件:
document.addEventListener('JsBridgeReady', function(e){
e.jsBridge.readyState
}.false);
在此时间之前,JsBridge 对象不存在,防止过早发送消息导致业务失败。
发送逻辑上允许 iOS 同时有多个数据被发送,因此要有发送队列。
因为 setTimeout 可能被取消的缘故,接收也应该是队列。