理解JSBridge

随着HTML5的不断普及及优化,其在移动端开发的应用也越来越多,在开发者我们经常需要web和native进行交互,也就是要用到经常听到的“JSBridge”。这里我就自己的理解及实践,讲一下什么是JSBridge。

有些朋友听到JSBridge,会觉得是个蛮高大上的东西,好像很厉害很复杂的样子,有了它web和native都能交互了呢。其实并非如此,相信我,JSBridge真的是一个非常非常简单的东西。

1. 为什么需要JSBridge?

这个也许是废话,但我相信有些朋友虽然在用JSBridge,但连这一点都没搞明白。顾名思义,JSBridge是一座用JavaScript搭建起来的桥,一端是web,一端是native。我们搭建这座桥的目的也很简单,让native可以调用web的js代码,让web可以 “调用” 原生的代码。请注意这个我加了 引号的调用,它并不是直接调用,而是可以根据web和native约定好的规则来通知native要做什么,native可以更具这个来执行相应的代码。当然,让web可以随心所欲调用native代码也不是不可能,但我想没人愿意那么干,一来实现起来稍微麻烦些,二来也有很大的安全隐患。

2. 为什么是 “JS” Bridge

这个比较容易理解,web端支持JavaScript,native有webview,webview可以在所加载的页面加载完成后调用页面的JavaScript代码。所以,使用JavaScript就理所当然了。

3. native调用web原理

首先,native需要一个webview,这个webview有个方法叫

1
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

通过这个方法,我们可以执行一段JavaScript代码,并取得返回值。

4. web调用native原理

native的webview有个代理方法:

1
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

在这个方法里可以拦截页面的请求,我们就在这里做文章。当web端加载一个链接,我们就在这个方法里面进行拦截,检查是否符合我们约定的规则,符合的话就拒绝加载并按这个链接携带的信息来执行相应的代码,不符合约定的规则就正常加载链接。
一般来说正常的请求都是http协议或者https协议,所以我们通常定义一个其他的协议以方便区分,比如“jsb”。我们来约定一个规则:需要native做的事情,就用“action”作为参数名,各action对应的其他参数分别约定。我们举两个例子。

例子1

我们约定一个名为back的action,无参数。web端需要加载的链接则为:

1
jsb://action=back

当native拦截到请求并发现是jsb协议,知道这是web发来任务了,解析后发现是一个back行为,于是执行了一段返回的代码。

例子2

我们约定一个名为shopdetail的action,用来跳转到商铺详情页面,需要带一个名字是id的参数,id为商铺id。web端需要加载的链接则为:

1
jsb://action=shopdetail&id=1000000

当native拦截到请求并发现是jsb协议,知道这是web发来任务了,解析后发现是一个shopdetail行为,一是执行了显示商铺详情的代码,并根据商铺id1000000去获取商铺详情。

5. 高级封装

上面的例子只是对JSBridge的简单用法,我们也可以进行一些高级的封装,以实现更合理的对话机制。比如我们在web调用native时,约定一个callback参数,用于native通知web代码执行结果;对参数直接约定为json字符串,以方便解析;对链接进行加密,以防止可能出现的攻击及增加安全性等。

6. web调用native的最佳实践

在实践中我们发现,在iOS9之前,如果连续的从web的js调用native,一些调用会被忽略掉(如采用window.location.href的方式)。最可靠的方法是使用iframe,创建一个iframe,指定其链接,增加到当前页面后马上将其删除,示例代码如下:

1
2
3
4
5
6
7
function execute(url) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", url);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}

热评文章