(转)iOS- JSBridge的原理

做者:心叶
时间:2019-03-25 10:18前端

原理概述

简介

JSBridge是Native代码与JS代码的通讯桥梁。目前的一种统一方案是:H5触发url scheme->Native捕获url scheme->原生分析,执行->原生调用h5。以下图:android

图片描述

url scheme介绍git

上图中有提到url scheme这个概念,那这究竟是什么呢?github

  • url scheme是一种相似于url的连接,是为了方便app直接互相调用设计的web

    • 具体为,能够用系统的OpenURI打开一个相似于url的连接(可拼入参数),而后系统会进行判断,若是是系统的url scheme,则打开系统应用,不然找看是否有app注册这种scheme,打开对应app
    • 须要注意的是,这种scheme必须原生app注册后才会生效,如微信的scheme为(weixin://)
  • 而本文JSBridge中的url scheme则是仿照上述的形式的一种方式api

    • 具体为,app不会注册对应的scheme,而是由前端页面经过某种方式触发scheme(如用iframe.src),而后Native用某种方法捕获对应的url触发事件,而后拿到当前的触发url,根据定义好的协议,分析当前触发了那种方法,而后根据定义来执行等
  • 注意,iOS10之后,urlscheme必须符合url规范,不然会报错

实现流程

基于上述的基本原理,如今开始设计一种JSBridge的实现数组

实现思路

要实现JSBridge,咱们能够进行关键步骤分析微信

  • 第一步:设计出一个Native与JS交互的全局桥对象
  • 第二步:JS如何调用Native
  • 第三步:Native如何得知api被调用
  • 第四步:分析url-参数和回调的格式
  • 第五步:Native如何调用JS
  • 第六步:H5中api方法的注册以及格式

以下图:
图片描述网络

第一步:设计出一个Native与JS交互的全局桥对象

咱们规定,JS和Native之间的通讯必须经过一个H5全局对象JSbridge来实现,该对象有以下特色app

  • 该对象名为"JSBridge",是H5页面中全局对象window的一个属性
var JSBridge = window.JSBridge || (window.JSBridge = {});
  • 该对象有以下方法

    - registerHandler(String,Function) H5调用,注册本地JS方法,注册后Native可经过JSBridge调用。调用后会将方法注册到本地变量messageHandlers 中
    
    - callHandler(String,JSON,Function) H5调用,调用原生开放的api,调用后实际上仍是本地经过url scheme触发。调用时会将回调id存放到本地变量responseCallbacks中
    
    - _handleMessageFromNative(JSON) Native调用,原生调用H5页面注册的方法,或者通知H5页面执行回调方法
  • 如图

图片描述

第二步:JS如何调用Native

在第一步中,咱们定义好了全局桥对象,能够咱们是经过它的callHandler方法来调用原生的,那么它内部经历了一个怎么样的过程呢?以下:

callHandler函数内部实现过程

在执行callHandler时,内部经历了如下步骤:

  • (1)判断是否有回调函数,若是有,生成一个回调函数id,并将id和对应回调添加进入回调函数集合responseCallbacks中
  • (2)经过特定的参数转换方法,将传入的数据,方法名一块儿,拼接成一个url scheme
//url scheme的格式如
//基本有用信息就是后面的callbackId,handlerName与data
//原生捕获到这个scheme后会进行分析
var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data
  • (3)使用内部早就建立好的一个隐藏iframe来触发scheme
//建立隐藏iframe过程
var messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
document.documentElement.appendChild(messagingIframe);

//触发scheme
messagingIframe.src = uri;

注意点

注意,正常来讲是能够经过window.location.href达到发起网络请求的效果的,可是有一个很严重的问题,就是若是咱们连续屡次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。因此JS端发起网络请求的时候,须要使用iframe,这样就能够避免这个问题。

第三步:Native如何得知api被调用

在上一步中,咱们已经成功在H5页面中触发scheme,那么Native如何捕获scheme被触发呢?

根据系统不一样,Android和iOS分别有本身的处理方式

Android捕获url scheme

在Android中(WebViewClient里),经过shouldoverrideurlloading能够捕获到url scheme的触发

public boolean shouldOverrideUrlLoading(WebView view, String url){
    //读取到url后自行进行分析处理

    //若是返回false,则WebView处理连接url,若是返回true,表明WebView根据程序来执行url
    return true;
}

另外,Android中也能够不经过iframe.src来触发scheme,android中能够经过window.prompt(uri, "");来触发scheme,而后Native中经过重写WebViewClient的onJsPrompt来获取uri

iOS捕获url scheme

iOS中,UIWebView有个特性:在UIWebView内发起的全部网络请求,均可以经过delegate函数在Native层获得通知。这样,咱们能够在webview中捕获url scheme的触发(原理是利用 shouldStartLoadWithRequest)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];

    NSString *requestString = [[request URL] absoluteString];
    //获取利润url scheme后自行进行处理
   return YES;
}

以后Native捕获到了JS调用的url scheme,接下来就该到下一步分析url了

第四步:分析url-参数和回调的格式

在前面的步骤中,Native已经接收到了JS调用的方法,那么接下来,原生就应该按照定义好的数据格式来解析数据了

url scheme的格式,前面已经提到。Native接收到Url后,能够按照这种格式将回调参数id、api名、参数提取出来,而后按以下步骤进行

  • (1)根据api名,在本地找寻对应的api方法,而且记录该方法执行完后的回调函数id
  • (2)根据提取出来的参数,根据定义好的参数进行转化

    • 若是是JSON格式须要手动转换,若是是String格式直接可使用
  • (3)原生本地执行对应的api功能方法
  • (4)功能执行完毕后,找到此次api调用对应的回调函数id,而后连同须要传递的参数信息,组装成一个JSON格式的参数

    • 回调的JSON格式为:{responseId:回调id,responseData:回调数据}
    • responseId String型 H5页面中对应须要执行的回调函数的id,在H5中生成url scheme时就已经产生
    • responseData JSON型 Native须要传递给H5的回调数据,是一个JSON格式: {code:(整型,调用是否成功,1成功,0失败),result:具体须要传递的结果信息,能够为任意类型,msg:一些其它信息,如调用错误时的错误信息}
  • (5)经过JSBridge通知H5页面回调

    • 参考 第五步Native如何调用JS

第五步:Native如何调用JS

到了这一步,就该Native经过JSBridge调用H5的JS方法或者通知H5进行回调了,具体以下

//将回调信息传给H5
JSBridge._handleMessageFromNative(messageJSON);

如上,其实是经过JSBridge的_handleMessageFromNative传递数据给H5,其中的messageJSON数据格式根据两种不一样的类型,有所区别,以下

Native通知H5页面进行回调

数据格式为:上文中的回调的JSON格式

Native主动调用H5方法

Native主动调用H5方法时,数据格式是:{handlerName:api名,data:数据,callbackId:回调id}

  • handlerName String型 须要调用的,h5中开放的api的名称
  • data JSON型 须要传递的数据,固定为JSON格式(由于咱们固定H5中注册的方法接收的第一个参数必须是JSON,第二个是回调函数)

注意,这一步中,若是Native调用的api是h5没有注册的,h5页面上会有对应的错误提示。

另外,H5调用Native时,Native处理完毕后必定要及时通知H5进行回调,要否则这个回调函数不会自动销毁,多了后会引起内存泄漏。

第六步:H5中api方法的注册以及格式

前面有提到Native主动调用H5中注册的api方法,那么h5中怎么注册供原生调用的api方法呢?格式又是什么呢?以下

H5中注册供原生调用的API

//注册一个测试函数
JSBridge.registerHandler('testH5Func',function(data,callback){
    alert('测试函数接收到数据:'+JSON.stringify(data));
    callback&&callback('测试回传数据...');
});

如上述代码为注册一个供原生调用的api

H5中注册的API格式注意

如上代码,注册的api参数是(data,callback)

其中第一个data即原生传过来的数据,第二个callback是内部封装过一次的,执行callback后会触发url scheme,通知原生获取回调信息

思路

大体思路就是

  • h5调用Native的关键步骤进行拆分,由之前的直接传递url scheme变为传递一个统一的url scheme,而后Native主动获取传递的参数

    • 完善之前: H5调用Native->将全部参数组装成为url scheme->原生捕获scheme,进行分析
    • 完善之后: H5调用Native->将全部参数存入本地数组->触发一个固定的url scheme->原生捕获scheme->原生经过JSBridge主动获取参数->进行分析

实现

这种完善后的流程和之前有所区别,以下

JSBridge对象图解

图片描述

JSBridge实现完整流程

图片描述

注意
因为此次完善的核心是:Native主动调用JS函数,并获取返回值。而在Android4.4之前,Android是没有这个功能的,因此并不彻底适用于Android

因此通常会进行一个兼容处理,Android中采用之前的scheme传法,iOS使用完善后的方案(也便于4.4普及后后续的完善)

完整的JSBridge

上述分析了JSBridge的实现流程,那么实际项目中,咱们就应该结合上述两种,针对Android和iOS的不一样状况,统一出一种完整的方案,以下

完整调用流程图

图片描述

例子

基于上面的思想,我的在github上维护了一个用于学习的项目(非转载内容): https://github.com/yelloxing/...

不采用url scheme方式

前面提到的JSBridge都是基于url scheme的,但其实若是不考虑Android4.2如下,iOS7如下,其实也能够用另外一套方案的,以下:

  • Native调用JS的方法不变
  • JS调用Native是再也不经过触发url scheme,而是采用自带的交互,好比
  • Android中,原生经过 addJavascriptInterface开放一个统一的api给JS调用,而后将触发url scheme步骤变为调用这个api,其他步骤不变(至关于之前是url接收参数,如今变为api函数接收参数)
  • iOS中,原生经过JavaScriptCore里面的方法来注册一个统一api,其他和Android中同样(这里就不须要主动获取参数了,由于参数能够直接由这个函数统一接收)
相关文章
相关标签/搜索