一文让你彻底理解JSBridge

「时光不负,创作不停,本文正在参加2021年终总结征文大赛

什么是JSBridge

我们都知道,原生APP开发中有一个webview的组件(Android中是webview,iOS7以下有UIWebview,7以上有WKWebview),这个组件可以加载Html文件。

在Html5没有兴盛之前,加载的Html往往只能用来做一些简单的静态资源显示,但是H5大行其道以后,Html5中有很多新增的功能,炫酷的效果,特别是iOS中H5支持一直都很良好,Android 4.4以上支持也足够,所以这时候发现可以将一些主要的逻辑都用H5页面来编写,然后原生直接用webview加载显示,这样大大提高了开发效率,一次开发,两端(android、ios)通用,而且体验也很不错。

而JSBridge是H5与Native的通信桥梁,他允许H5去调用原生的api(比如拍照、截屏、录屏等),极大的拓展了H5功能。

image.png

通信原理

JSBridge通信方式的原理可以用一张图和一句话来概括,那就是

H5->通过某种方式触发一个url->Native捕获到url,进行分析->原生做处理->Native调用H5的JSBridge对象传递回调

image.png

url scheme介绍

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

具体为,可以用系统的OpenURI打开一个类似于url的链接(可拼入参数),然后系统会进行判断,如果是系统的 url scheme,则打开系统应用,否则找看是否有app注册这种scheme,打开对应app

需要注意的是,这种scheme必须原生app注册后才会生效,如微信的scheme为(weixin://)

  • 我们这里的JSBridge中的url scheme则是仿照上述的形式的一种方式

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

扫描二维码关注公众号,回复: 13471172 查看本文章

JSBridge设计思路

上面已经介绍了jsbridge的原理,那么要设计出一个jsbrdige对象,需要具备哪些功能呢

关键思路

  1. 设计出一个Native与JS交互的全局桥对象
  2. JS如何调用Native
  3. Native如何得知api被调用
  4. 分析url-参数和回调的格式
  5. Native如何调用JS
  6. H5中api方法的注册以及格式

image.png

了解完整体的思路,那么就让我们开始吧

1.设计一个native与JS交互的全局桥对象

  • 该对象名为"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页面执行回调方法

2.JS如何调用Native

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

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

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

//url scheme的格式如
//基本有用信息就是后面的callbackId,handlerName与data
//原生捕获到这个scheme后会进行分析
var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data
复制代码
  1. 使用内部早就创建好的一个隐藏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,这样就可以避免这个问题。

3.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后自行进行处理
复制代码

4.分析url-参数和回调的格式

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

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

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

  • 根据提取出来的参数,根据定义好的参数进行转化如果是JSON格式需要手动转换,如果是String格式直接可以使用

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

  • 功能执行完毕后,找到这次api调用对应的回调函数id,然后连同需要传递的参数信息,组装成一个JSON格式的参数回调的JSON格式为: {responseId:回调id,responseData:回调数据}

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

5.Native如何调用JS

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

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

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

Native通知H5页面进行回调

参考第4点 native功能执行完成后通知h5回调

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调用的api是h5没有注册的,h5页面上会有对应的错误提示。

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

6.H5中api方法的注册以及格式

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

H5中注册供原生调用的API

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

进一步JSBridge完善方案......

大致思路就是

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

完善以前: H5调用Native->将所有参数组装成为url scheme->原生捕获scheme,进行分析

完善以后: H5调用Native->将所有参数存入本地数组->触发一个固定的url scheme->原生捕获scheme->原生通过JSBridge主动获取参数->进行分析

最后附上demo链接,喜欢的小伙伴给个赞哦~

未完待续...

猜你喜欢

转载自juejin.im/post/7036988966356123662