聊一聊桥接(JSBridge)的原理

1、前言

现在的互联网时代也称移动互联网时代,基本上每一个人天天都会花费大量时间在移动设备上,早期的移动端应用大都使用原生开发(android,ios),而如今的移动开发技术选型上基本都是混合开发(Hybrid),混合开发是一种开发模式,指使用多种开发模型开发App,一般会涉及到两大类技术:原生Native、Web H5android

原生技术主要指iOS(Objective C)、Android(Java),原生开发效率较低,开发完成须要从新打包整个App,发布依赖用户的更新,性能较高功能覆盖率更高,发布流程较为复杂
Web H5主要由HTML、CSS、JavaScript组成,Web能够更好的实现发布更新,跨平台也更加优秀,但性能较低,功能实现也受限
ios

混合开发的意义就在于吸收二者的优势,并且随着手机硬件的升级迭代、系统(Android 5.0+、ISO 9.0+)对于Web特性的较好支持,H5的劣势被逐渐缩小web

原生、Web相互通讯都离不开JSBridgeajax

2、JSBridge作了些什么?

Hybrid模式下,H5会常常须要使用Native的功能,好比打开二维码扫描、调用原生页面、获取用户信息等,同时Native也须要向Web端发送推送、更新状态等,而JavaScript是运行在单独的JS Context(Webview容器、JSCore等),与原生有运行环境的隔离,因此须要有一种机制实现Native端和Web端的双向通讯,这就是JSBridge以JavaScript引擎或Webview容器做为媒介,经过协定协议进行通讯,实现Native端和Web端双向通讯的一种机制。浏览器

经过JSBridge,Web端能够调用Native端的Java接口,一样Native端也能够经过JSBridge调用Web端的JavaScript接口,实现彼此的相互调用。网络

3、什么是WebView?

首先了解下webViewwebView是移动端(原生)提供的运行JavaScript的环境,它是一种嵌入式浏览器,原生应用能够用它来展现网络内容。可与页面JavaScript交互,实现混合开发,其中AndroidiOS又有些不一样:异步

AndroidWebView采用的是低版本和高版本使用了不一样的webkit内核,4.4后直接使用了Chrome。ide

iOSUIWebView算是自IOS2就有,但性能较差,特性支持较差,WKWebViewiOS8以后的升级版,性能更强特性支持也较好。函数

WebView控件除了能加载指定的url外,还能够对URL请求、JavaScript的对话框、加载进度、页面交互进行强大的处理,以后会提到拦截请求、执行JS脚本都依赖于此。性能

4、JSBridge的实现原理

Web端和Native能够类比于Client/Server模式,Web端调用原生接口时就如同Client向Server端发送一个请求相似,JSBridge在此充当相似于HTTP协议的角色,实现JSBridge主要是两点:

  • 将Native端原生接口封装成JavaScript接口
  • 将Web端JavaScript接口封装成原生接口
4.1 Native->Web

首先来讲Native端调用Web端,这个比较简单,JavaScript做为解释性语言,最大的一个特性就是能够随时随地地经过解释器执行一段JS代码,因此能够将拼接的JavaScript代码字符串,传入JS解析器执行就能够,JS解析器在这里就是webView。

4.2 Web->Native

Web调用Native端主要有两种方式

  • 4.2.1 拦截Webview请求的URL Schema
    URL Schema是类URL的一种请求格式,格式以下:
<protocol>://<host>/<path>?<qeury>#fragment

咱们能够自定义JSBridge通讯的URL Schema,好比:jsbridge://showToast?text=hello
Native加载WebView以后,Web发送的全部请求都会通过WebView组件,因此Native能够重写WebView里的方法,拦截Web发起的请求,咱们对请求的格式进行判断:
若是符合咱们自定义的URL Schema,对URL进行解析,拿到相关操做、操做,进而调用原生Native的方法
若是不符合咱们自定义的URL Schema,咱们直接转发,请求真正的服务

Web发送URL请求的方法有这么几种:

a标签
location.href
使用iframe.src
发送ajax请求
这些方法,a标签须要用户操做,location.href可能会引发页面的跳转丢失调用,发送ajax请求Android没有相应的拦截方法,因此使用iframe.src是常常会使用的方案

安卓提供了shouldOverrideUrlLoading方法拦截
IOSUIWebView使用shouldStartLoadWithRequest
IOSWKWebView则使用decidePolicyForNavigationAction
这种方式从早期就存在,兼容性很好,可是因为是基于URL的方式,长度受到限制并且不太直观,数据格式有限制,并且创建请求有时间耗时。

  • 4.2.2 向Webview中注入JS API
    这个方法会经过webView提供的接口,App将Native的相关接口注入到JSContext(window)的对象中,通常来讲这个对象内的方法名与Native相关方法名是相同的,Web端就能够直接在全局window下使用这个全局JS对象,进而调用原生端的方法。

Android(4.2+)提供了addJavascriptInterface注入:

// 注入全局JS对象
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");

class NativeBridge {
  private Context ctx;
  NativeBridge(Context ctx) {
    this.ctx = ctx;
  }

  // 增长JS调用接口
  @JavascriptInterface
  public void showNativeDialog(String text) {
    new AlertDialog.Builder(ctx).setMessage(text).create().show();
  }
}

在Web端直接调用这个方法便可:

window.NativeBridge.showNativeDialog('hello');
4.3 带回调的调用

上面已经说到了Native、Web间双向通讯的两种方法,但站在一端而言仍是一个单向通讯的过程 ,好比站在Web的角度:Web调用Native的方法,Native直接相关操做但没法将结果返回给Web,但实际使用中会常常须要将操做的结果返回,也就是JS回调。

因此在对端操做并返回结果,有输入有输出才是完整的调用,那如何实现呢?

其实基于以前的单向通讯就能够实现,咱们在一端调用的时候在参数中加一个callbackId标记对应的回调,对端接收到调用请求后,进行实际操做,若是带有callbackId,对端再进行一次调用,将结果、callbackId回传回来,这端根据callbackId匹配相应的回调,将结果传入执行就能够了。

5、开源的JSBridge

实现一个完整的JSBridge是比较复杂的,须要考虑一些低端机型的兼容问题、同步异步调用问题,好在已经有开源的JSBridge供咱们直接使用了:

DSBridge,主要经过注入API的形式,DSBridge for Android、DSBridge for IOS
JsBridge,主要经过拦截URL Schema,JsBridge
以DSBridge-Android为例:

// Web端代码
<body>
  <div>
    <button id="showBtn">获取Native输入,以Web弹窗展示</button>
  </div>
</body>
// 引入SDK
<script src="https://unpkg.com/dsbridge@3.1.3/dist/dsbridge.js"></script>
<script>
  const showBtn = document.querySelector('#showBtn');
  showBtn.addEventListener('click', e => {
    // 注意,这里代码不一样:SDK在全局注册了dsBridge,经过call调用Native方法
    dsBridge.call('getNativeEditTextValue', '', value => {
      window.alert('Native输入值' + value);
    })
  });
</script>
// Android代码
// 使用dwebView替换原生webView
dwebView.addJavascriptObject(new JsApi(), null);

class JSApi {
  private Context ctx;
  public JSApi (Context ctx) {
    this.ctx = ctx;
  }

  @JavascriptInterface
  public void getNativeEditTextValue(Object msg, CompletionHandler<String> handler) {
    String value = ((MainActivity)ctx).editText.getText().toString();
    // 经过handler将value传给Web端,实现回调的JSB调用
    handler.completed(value);
  }
}

Hybrid开发是目前移动端开发的主流技术选项,其中Native和Web端的双向通讯就离不开JSBridge
其中Native调用Web端是直接在JS的Context直接执行JS代码,Web端调用Native端有两种方法,一种是基于URL Schema的拦截操做,另外一种是向JS的Context(window)注入Api,其中注入Api是目前最好的选择。完整的调用是双向通讯,须要一个回调函数,技术实现上就是使用了两次单向通讯

开源的JSBridge:DSBridge、jsBridge。

相关文章
相关标签/搜索