Hybrid开发——Native与H5的通信框架JSBridge实现(1)

特别说明


首先说明本文并非原创,原文在此:https://www.cnblogs.com/dailc/p/5931324.html#callback-format。相关代码会放在https://github.com/Samuel2306/JSBridge。


什么是JSBridge


小伙伴如果如果对于Hybrid并没有什么概念,请先阅读笔者的另外一篇文章《Hybrid开发——Android与H5的亲密接触》。JSBridge就是一个简化Native与JS通信的框架。JSBridge定义了统一的通信过程:Native只通过一个固定的桥对象调用JS,JS也只通过固定的桥对象调用Native,具体流程如下图所示:

Hybrid开发——Native与H5的通信框架JSBridge实现(1)_第1张图片
JSBridge定义的通信过程

说明:JS触发原生的回调函数的过程其实也是触发一个url schema


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,根据定义好的协议,分析当前触发了那种方法。简而言之就是url => 方法(对应关系是JS开发与Android开发商量定义好的)。


为什么要用JSBridge

在笔者的《Hybrid开发——Android与H5的亲密接触》一文中,我们知道Android已经可以跟JS进行交互,那为什么还要这种通过url scheme的JSBridge方式呢,原因大致如下:

• Android4.2以下,addJavascriptInterface方式有安全漏掉

• iOS7以下, JS无法调用Native

url scheme交互方式是一套现有的成熟方案,可以完美兼容各种版本,不存在上述问题。而且JSBridge将Native与JS的交互方式进行解耦,提供了一个可扩展的,高可用的,稳定的解决方案。


实现一个JSBridge


实现步骤大致如下:

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

第二步: JS调用Native功能实现

第三步: Native监听api调用

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

第五步: Native调用JS功能实现

第六步: H5中api方法的注册

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

我们规定,JS和Native之间的通信必须通过一个H5全局对象JSBridge来实现,该全局对象有如下特点:

• 该对象名为“JSBridge”,是H5页面中全局对象window的一个属性

window.JSBridge = JSBridge

• 该对象有如下方法

1、registerHandler( String handlerName, Function handler):供H5调用。用来注册JS方法,Native可通过JSBridge调用注册的JS方法。调用registerHandler方法后,被注册的方法 handler 会被保存到本地变量 messageHandlers 中。

2、callHandler( String handlerName, JSON data, Function callback):供H5调用。H5调用原生开放的api,调用后实际上还是本地通过url scheme触发。调用时会将回调 callback 的 id(由callHandler函数生成)存放到本地变量responseCallbacks中。

3、_handleMessageFromNative( JSON ):供Native调用。原生调用H5页面注册的方法,或者通知H5页面执行回调方法(H5调用原生方法时,会将回调函数作为参数进行传递,就是为了让原生能在适当的时候通过JSBridge调用回调函数,也就是responseCallbacks里面对应的函数)。

Hybrid开发——Native与H5的通信框架JSBridge实现(1)_第2张图片
对象功能结构图


第二步: JS调用Native功能实现

上一步我们已经定义好了JSBridge对象,H5可以通过callHandler方法来调用原生的API,那么callHandler内部经历了一个怎么样的过程呢?接下来我们就来说说callHandler( String handlerName, JSON data, Function callback)函数内部实现过程。

在执行callHandler( String handlerName, JSON data, Function callback)时,内部经历了以下步骤:

(1)首先判断H5调用该方法的时候有没有传入回调函数。如果有回调函数,则生成一个回调函数id, 并将id和对应的回调函数添加进入回调函数的集合 responseCallbacks 中

(2)通过特定的参数转换方法,将传入的数据(data), 方法名(handlerName)一起,拼接成一个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;

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

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

第三步: Native监听api调用

在上一步中,我们已经成功在H5页面中触发scheme,那么Native如何捕获scheme被触发呢?接下来我们以Android为例,说说Android如何捕获url scheme,不过在此之前我们先来简单了解几个东西:WebView、WebViewClient、WebChromeClient。

在Android的Webview设计中,不是所有功能都由WebView类来实现的,部分功能由其他的类(WebViewClient、WebChromeClient)来实现,这样一来WebView主要专心干好解析、渲染工作就行了。

WebViewClient帮助WebView处理各种通知、请求事件的,具体来说包括:onLoadResource、onPageStart、onPageFinish、onReceiveError、onReceivedHttpAuthRequest;

WebChromeClient辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度。具体来说包括onCloseWindow(关闭WebView)、onCreateWindow()、onJsAlert (WebView上alert是弹不出来东西的,需要定制你的WebChromeClient处理弹出)、onJsPrompt、onJsConfirm、onProgressChanged、onReceivedIcon、onReceivedTitle;

在Android中通过 WebViewClient 的 shouldoverrideurlloading 可以捕获到url scheme的触发:

// WebViewClient主要帮助WebView处理各种通知、请求事件

private WebViewClient webViewClient = new WebViewClient() {

    @Override // 重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示 public     boolean shouldOverrideUrlLoading(WebView view, String url) {

        // 原生通过解析url进行相应处理

    }

};

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


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

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

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

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

(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通过JSBridge调用H5的JS方法或者通知H5进行回调了,具体如下:

JSBridge._handleMessageFromNative(messageJSON);


messageJSON数据格式根据两种不同的类型,有所区别:

Native通知H5页面进行回调,messageJSON数据格式如下:

 {

    responseId: 回调id,  

    responseData: 回调数据

}

Native主动调用H5方法,messageJSON数据格式如下:

{

    handlerName:  需要调用的h5 api的名称

    data:  需要传递的数据,固定为JSON格式

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

}


第六步: H5中api方法的注册


//注册一个测试函数

JSBridge.registerHandler('testH5Func',  function(data,  callback){

    alert('测试函数接收到数据:'+JSON.stringify(data));

    callback&&callback('测试回传数据...');

});

data: 原生传过来的数据;

callback: 内部封装过一次的, 执行callback后会触发url scheme, 通知原生获取回调信息。


结语


自此,我们已经将JSBridge的整体架构和实现思路都讲明白了,本篇文章是关于JS部分的实现。

你可能感兴趣的:(Hybrid开发——Native与H5的通信框架JSBridge实现(1))