天天看点

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

做过混合开发的人都知道ionic和phonegap之类的框架,这些框架在web基础上包装一层native,然后通过bridge技术的js调用本地的库。

在讲jsbridge技术之前,我们来看一下传统的实现方式。

native调用js比较简单,只要遵循:"javascript: 方法名('参数,需要转为字符串')"的规则即可。

在4.4之前,调用的方式:

4.4以后(包括4.4),使用以下方式:

说明:

4.4之前native通过loadurl来调用js方法,只能让某个js方法执行,但是无法获取该方法的返回值

4.4之后,通过evaluatejavascript异步调用js方法,并且能在onreceivevalue中拿到返回值

不适合传输大量数据(大量数据建议用接口方式获取)

mwebview.loadurl("javascript: 方法名('参数,需要转为字符串')");函数需在ui线程运行,因为mwebview为ui控件

js调用native需要对webview设置@javascriptinterface注解,这里有个漏洞,后面会给大家说明。要想js能够native,需要对webview设置以下属性。

这里我们看到了getjsbridge(),native中通过addjavascriptinterface添加暴露出来的js桥对象,然后再该对象内部声明对应的api方法。

那么html怎么调用native的方法呢?

在android4.2以上(api17后),暴露的api要加上注解@javascriptinterface,否则会找不到方法。

在api17以前,addjavascriptinterface有风险,hacker可以通过反编译获取native注册的js对象,然后在页面通过反射java的内置 静态类,获取一些敏感的信息和破坏

js调用native暴露的api,并且能得到相应返回值

native调用js的方法比较简单,native通过stringbyevaluatingjavascriptfromstring调用html绑定在window上的函数。不过应注意oc和swift的写法。

native调用js方法时,能拿到js方法的返回值

native中通过引入官方提供的javascriptcore库(ios7以上),然后可以将api绑定到jscontext上(然后html中js默认通过window.top.*可调用)。

引入官方的库文件

native注册api函数(oc)

html中js调用native方法

ios7才出现这种方式,在这之前,js无法直接调用native,只能通过jsbridge方式简介调用

js能调用到已经暴露的api,并且能得到相应返回值

ios原生本身是无法被js调用的,但是通过引入官方提供的第三方"javascriptcore",即可开放api给js调用

jsbridge:听其取名就是js和native之前的桥梁,而实际上jsbridge确实是js和native之前的一种通信方式。简单的说,jsbridge就是定义native和js的通信,native只通过一个固定的桥对象调用js,js也只通过固定的桥对象调用native。jsbridge另一个叫法及大家熟知的hybrid app技术。

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

流程:h5->通过某种方式触发一个url->native捕获到url,进行分析->原生做处理->native调用h5的jsbridge对象传递回调。

我们前面讲过了原生的webview/uiwebview控件已经能够和js实现数据通信了,那为什么还要jsbridge呢?

其实使用jsbridge有很多方面的考虑:

android4.2以下,addjavascriptinterface方式有安全漏掉

ios7以下,js无法调用native

url scheme交互方式是一套现有的成熟方案,可以完美兼容各种版本,对以前老版本技术的兼容。

url scheme是一种类似于url的链接,是为了方便app直接互相调用设计的。具体来讲如果是系统的url scheme,则打开系统应用,否则找看是否有app注册这种scheme,打开对应app。

注:这种scheme必须原生app注册后才会生效。

而在我们实际的开发中,app不会注册对应的scheme,而是由前端页面通过某种方式触发scheme(如用iframe.src),然后native用某种方法捕获对应的url触发事件,然后拿到当前的触发url,根据定义好的协议,分析当前触发了那种方法。

要实现jsbridge,我们需要按以下步骤分析:

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

第二步:js如何调用native

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

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

第五步:native如何调用js

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

jsbridge的完整流程可总结为:

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

我们规定,js和native之间的通信必须通过一个h5全局对象jsbridge来实现。该对象有如下特点:

该对象名为"jsbridge",是h5页面中全局对象window的一个属性,形如:

该对象有如下方法:

registerhandler( string,function )h5调用注册本地js方法,注册后native可通过jsbridge调用。调用后会将方法注册到本地变量messagehandlers 中。

callhandler( string,json,function )h5调用 调用原生开放的api,调用后实际上还是本地通过url scheme触发。调用时会将回调id存放到本地变量responsecallbacks中

_handlemessagefromnative( json )native调用 原生调用h5页面注册的方法,或者通知h5页面执行回调方法

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

我们定义好了全局桥对象,可以通过它的callhandler方法来调用原生的api。

在执行callhandler时,内部经历了以下步骤:

判断是否有回调函数,如果有,生成一个回调函数id,并将id和对应回调添加进入回调函数集合responsecallbacks中。

通过特定的参数转换方法,将传入的数据,方法名一起,拼接成一个url scheme

使用内部早就创建好的一个隐藏iframe来触发scheme

注:正常来说是可以通过window.location.href达到发起网络请求的效果的,但是有一个很严重的问题,就是如果我们连续多次修改window.location.href的值,在native层只能接收到最后一次请求,前面的请求都会被忽略掉。所以js端发起网络请求的时候,需要使用iframe,这样就可以避免这个问题。

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

根据系统不同,android和ios分别有自己的处理方式。

在android中(webviewclient里),通过shouldoverrideurlloading可以捕获到url scheme的触发。

ios中,uiwebview有个特性:在uiwebview内发起的所有网络请求,都可以通过delegate函数在native层得到通知。这样,我们可以在webview中捕获url scheme的触发(原理是利用 shouldstartloadwithrequest)

在前面的步骤中,native已经接收到了js调用的方法,那么接下来,原生就应该按照定义好的数据格式来解析数据了,native接收到url后,可以按照这种格式将回调参数id、api名、参数提取出来,然后按如下步骤进行。

根据api名,在本地找寻对应的api方法,并且记录该方法执行完后的回调函数id

根据提取出来的参数,根据定义好的参数进行转化

原生本地执行对应的api功能方法

功能执行完毕后,找到这次api调用对应的回调函数id,然后连同需要传递的参数信息,组装成一个json格式的参数

通过jsbridge通知h5页面回调

到了这一步,就该native通过jsbridge调用h5的js方法或者通知h5进行回调了。其中的messagejson数据格式根据两种不同的类型。

native通知h5页面进行回调:

数据格式为: native通知h5回调的json格式。

native主动调用h5方法:

native主动调用h5方法时,数据格式是:{handlername:api名,data:数据,callbackid:回调id}:

handlername string型 需要调用的,h5中开放的api的名称

data json型 需要传递的数据,固定为json格式(因为我们固定h5中注册的方法接收的第一个参数必须是json,第二个是回调函数)

callbackid string型 原生生成的回调函数id,h5执行完毕后通过url scheme通知原生api成功执行,并传递参数

前面有提到native主动调用h5中注册的api方法,那么h5中怎么注册供原生调用的api方法呢?

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

jsbridge对象图解:

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

jsbridge实现完整流程:

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

那么我们在实际的开发中,如何针对android和ios的不同情况,统一出一种完整的方案。

JSBridge深度剖析概述JSBridgeJSBridge技术实现总结

前面提到的jsbridge都是基于url scheme的,但其实如果不考虑android4.2以下,ios7以下,其实也可以用另一套方案的。

native调用js的方法不变

js调用native是不再通过触发url scheme,而是采用自带的交互

具体来讲:

android中,原生通过 addjavascriptinterface开放一个统一的api给js调用,然后将触发url scheme步骤变为调用这个api,其余步骤不变。

os中,原生通过javascriptcore里面的方法来注册一个统一api,其余和android中一样。

继续阅读