NWBridge

Connecting native and webview

View the Project on GitHub yanni4night/NWBridge

Library for Hybrid

为什么需要 JavaScript Lib?

Android 可以直接向 Webview 中注入 JavaScript 对象,但是该对象仅直接纯函数调用,同时在 4.2 版本以下存在安全漏洞

iOS 则无法注入任何 JavaScript 对象。

于是面向业务需要一套 SDK 能够:

为什么不使用 WebViewJavascriptBridge

通信原理(Principle of Communication)

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 来抓取数据。

消息(Message)

消息是由消息类型、请求数据、响应数据、callback ID等构成的纯 JavaScript 对象。

{
  messageType:
  inputData:
  outputData:
    errNo:
    errMsg:
    data:
  callbackId:
}

消息的流转全部通过一个发送队列和一个接收队列。

这里仿照 TCP 的 ACK 包,要求每一个请求消息都必须有 callback 回复确认。

时序图(Sequence)

Native 向 Webview 发送请求

Webview 向 Native 发送请求(Android)

Webview 向 Native 发送请求(iOS)

模型(Model)

指令(command/cmd)

API 的命名空间,Native/Webview 意义相同,不公用。

方法(method)

API 的方法名,Native/Webview 意义相同,不公用。

异步回调

允许被调用方以异步的方式响应请求,这可能会受到调用方超时时间的限制。

安装(Install)

直接源码构建:

$ npm install
$ make

或者通过 NPM:

npm install --save nwbridge

用法(Usage)

调用 NWBridge 构造函数。

new NWBridge('__tb_js_bridge', 'JsBridge', 'schema://', hybridInitialData)

上面的代码会在全局挂在两个变量,__tb_js_bridgeJsBridge,相同变量名重复构造无效。

如果你是通过 NPM 安装,那么你可能需要:

import NWBridge from 'nwbridge'

应用开发接口(Webview API)

以下所有 API 都挂在同一变量下,命名允许配置,由 Webview 业务端代码(Js)调用。为简便起见,这里以 JsBridge 为例。注意 JsBridge 对象不一定存在。

readyState 属性

该属性指示访问 Native 的能力状态,可能有三个取值:pendingcomplete、error只有当该值为 complete 时以下方法才存在

alert(JsBridge.readyState)

call(command, method, payload, timeout)

访问 Native 的预定义方法,返回一个 Promise 对象。

JsBridge.call('dialog', 'confirm', {msg: 'R u sure?'}, 3000)
  .then(function (yes) {});

register(command, method, function, isAsync)

注册一个在 Webview 定义的供 Native 调用的 API。

JsBridge.register('location', 'search',
  function(){
    return location.search;
  }, false);

如果是异步的:

JsBridge.register('location', 'search',
  function(cb){
    cb(location.search);
  }, true);

应用开发接口(Native API)

以下所有 API 都挂在同一变量下,命名允许配置,由 Native 代码(Java/Object-C)调用。为简便起见,这里以 __js_bridge 为例。

send(message)

向 Webview 发送消息,参数必须是合法 JSON 语法的字符串形式,必须符合上面 Message 的格式,如:

__js_bridge.send('{"messageType": "request"}')

fetch()

iOS 端用来从 Webview 主动抓取数据的方法,不存在于 Android 端。返回值为字符串类型,为合法的 JSON 数组类型,数组中每个成员为上面的 Message 格式。

__js_bridge.fetch()

FAQ

1. JsBridge 中的 setTimeout 是做什么的?

Native 直接访问 JavaScript,会被 Webcore 的单线程 hang 住,为了不影响客户端主线程的渲染,这里使用 setTimeout 或 setImmediate 将后续逻辑发到下一个执行周期中。

注意这个定时器在某些情况下可能被取消,因此只要没被取消,应尽可能地处理数据,避免“失活”。

2. 如果得到 JsBridge 对象准备好的通知?

使用 document 事件:

document.addEventListener('JsBridgeReady', function(e){
  e.jsBridge.readyState
}.false);

在此时间之前,JsBridge 对象不存在,防止过早发送消息导致业务失败。

3. 收到/产生一个消息就处理/发送一个消息,为什么还要使用队列?

发送逻辑上允许 iOS 同时有多个数据被发送,因此要有发送队列。

因为 setTimeout 可能被取消的缘故,接收也应该是队列。