专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

前端和App开发者都要懂的JSBridge的原理

ins518 2024-12-26 13:54:59 技术文章 14 ℃ 0 评论

众所周知,app的一些功能可能会使用到H5开发,这就难免会遇到java与js的相互调用,像android利用WebViewJavascriptBridge实现js和java的交互,这里主要介绍下JsBridge的原理和使用。



一、什么是JSBridge

JSBridge主要是给JavaScript提供调用Native功能的接口,让混合开发中的前端部分可以方便地使用Native的功能(例如:地址位置、摄像头)。而且JSBridge的功能不止调用Native功能这么简单宽泛。

JSBridge是一座用JavaScript搭建起来的桥,一端是Web,一端是Native,是Native和Web之间的桥梁。我们搭建这座桥的目的也很简单,让Native可以调用Web的js代码,让Web可以“调用”原生的代码。它的核心是构建Native和Web间消息通信的通道,而且这个通信的通道是双向的。


双向通信的通道:

JS向Native发送消息:调用相关功能、通知Native当前JS的相关状态等。

Native向JS发送消息:回溯调用结果、消息推送、通知JS当前Native的状态等。

H5与Native交互如下图:



二、JSBridge的实现原理

JavaScript是运行在一个单独的JS Context中(例如WebView的Webkit引擎、JSCore)。由于这些Context与原生运行环境的天然隔离,我们可以将这种情况与RPC(Remote Procedure Call,远程过程调用)通信进行类比,将Native与JavaScript的每次互相调用看做一次RPC调用。

在JSBridge的设计中,可以把前端看做RPC的客户端,把Native端看做RPC的服务器端,从而JSBridge要实现的主要逻辑就出现了:通信调用(Native与JS通信)和句柄解析调用



三、JSBridge的通信原理

1.JavaScript调用Native的方式

主要有两种:注入API和拦截URL SCHEME

1.1 注入API

注入API方式的主要原理是,通过WebView提供的接口,向JavaScript的Context(window)中注入对象或者方法,让JavaScript调用时,直接执行相应的Native代码逻辑,达到JavaScript调用Native的目的。

iOS

UIWebVIew(iOS2+)和WKWebView(iOS8+)的调用方式有所区别

//假设ios客户端约定方法名为nativeBridge

//UIWebView
window.nativeBridge(message);
//WKWebView
window.webkit.messageHandlers.nativeBridge.postMessage(message);

Android

原理:通过WebView提供的addJavascriptInterface方法给浏览器window注入一个命名空间,然后给Web增加一些可以操作Java的反射。

//addJavascriptInterface
mWebView.addJavascriptInterface(new Class(), 'android');  

//@JavascriptInterface
public class Class(){
  @JavascriptInterface
  public void method(){

  }
}
//js 代码
window.android.method();

备注:在4.2之前,Android注入JavaScript对象的接口是addJavascriptInterface,但是这个接口有漏洞,可以被不法分子利用,危害用户的安全,因此在4.2中引入新的接口@JavascriptInterface(上面代码中使用的)来替代这个接口,解决安全问题。

1.2 拦截URL SCHEME

解释一下URL SCHEME:URL SCHEME是一种类似于url的链接,是为了方便app直接互相调用设计的,形式和普通的url近似,主要区别是protocol和host一般是自定义的。

例如:
esign://home/url?url=www.baidu.com,
protocol是esign,host则是home。

拦截URL SCHEME 的主要流程是:Web端通过某种方式(例如iframe.src)发送URLScheme请求,之后Native拦截到请求并根据URL SCHEME(包括所带的参数)进行相关操作。

在时间过程中,这种方式有一定的缺陷:

  • 使用iframe.src发送URLSCHEME会有url长度的隐患。
为什么选择iframe.src不选择locaiton.href?
因为如果通过location.href连续调用Native,很容易丢失一些调用。
  • 创建请求,需要一定的耗时,比注入API的方式调用同样的功能,耗时会较长。
  • 因此:JavaScript调用Native推荐使用注入API的方式



    2.Native调用JavaScript的方式

    相比于JavaScript调用Native,Native调用JavaScript较为简单,直接执行拼接好的JavaScript代码即可。

    从外部调用JavaScript中的方法,因此JavaScript的方法必须在全局的window上。

    iOS

    对于iOS的UIWebView,示例如下:

    //UIWebView
    result = [uiWebview stringByEvaluatingJavaScriptFromString:javaScriptString];

    对于iOS的WKWebView,示例如下:

    //WKWebView
    [wkWebView evaluateJavaScript:javaScriptString completionHandler:completionHandler];

    Android

    Android

    在Kitkat(4.4)之前是使用webview的loadUrl进行调用的:

    webView.loadUrl("javascript:JSBridge.trigger('webviewReady')");

    而Kitkat之后的版本,也可以用evaluateJavascript方法实现:

    webView.evaluateJavascript(javaScriptString,new ValueCallback<String>() {
        @Override
        publicvoidonReceiveValue(String value){
    
        }
    });



    四、JSBridge 接口实现

    从上面的剖析中,可以得知,JSBridge的接口主要功能有两个:调用Native(给Native发消息)和接被Native调用(接收Native消息)。因此,JSBridge可以设计如下:

    window.JSBridge = {
        // 调用 Native
        invoke: function(msg) {
            // 判断环境,获取不同的 nativeBridge
            nativeBridge.postMessage(msg);
        },
        receiveMessage: function(msg) {
            // 处理 msg
        }
    };

    在上面部分中,提到过RPC中有一个非常重要的环节是句柄解析调用,这点在JSBridge中体现为句柄与功能对应关系。同时,我们将句柄抽象为桥名(BridgeName),最终演化为一个BridgeName对应一个Native功能或者一类Native消息。基于此点,JSBridge的实现可以优化为如下:

    window.JSBridge = {
        // 调用 Native
        invoke: function(bridgeName, data) {
            // 判断环境,获取不同的 nativeBridge
            nativeBridge.postMessage({
                bridgeName: bridgeName,
                data: data || {}
            });
        },
        receiveMessage: function(msg) {
            var bridgeName = msg.bridgeName,
                data = msg.data || {};
            //具体逻辑
        }
    };
    

    终极提问:消息都是单向的,那么调用Native功能时Callback怎么实现的?

    对于JSBridge的Callback,其实就是RPC框架的回调机制。当然也可以用更简单的JSONP机制解释:

    当发送JSONP请求时,url参数里会有callback参数,其值是当前页面唯一的,
    而同时以此参数值为key将回调函数存到window上,随后,
    服务器返回script中,也会以此参数值作为句柄,调用相应的回调函数。

    整体流程:

    在Native端配合实现JSBridge的JavaScript调用Native逻辑也很简单,主要的代码逻辑是:接收到JavaScript消息=>解析参数,拿到bridgeName、data和callbackId=>根据bridgeName找到功能方法,以data为参数执行=>执行返回值和callbackId一起回传前端。

    Native调用JavaScript也同样简单,直接自动生成一个唯一的ResponseId,并存储句柄,然后和data一起发送给前端即可。



    五、JSBridge的总结

    对于JSBridge的引用,常用有如下两种方式,但各有利弊。

    1.由Native端进行注入

    注入方式和Native调用JavaScript类似,直接执行桥的全部代码。

    它的优点是:

    桥的版本很容易与Native保持一致,Native端不用对不同版本的JSBridge进行兼容。

    它的缺点是:

    注入时机不确定,需要实现注入失败后重试的机制,保证注入的成功率,同时JavaScript端在调用接口时,需要优先判断JSBridge是否已经注入成功。

    2.由JavaScript端引用

    直接与JavaScript一起执行。

    它的优点是:

    JavaScript端可以确定JSBridge的存在,直接调用即可。

    它的缺点是:

    如果桥的实现方式有更改,JSBridge需要兼容多版本的Native Bridge或者Native Bridge兼容多版本的JSBridge。

    至此,JSBridge的原理介绍完毕,欢迎大家转发留言进行交流。

    Tags:

    本文暂时没有评论,来添加一个吧(●'◡'●)

    欢迎 发表评论:

    最近发表
    标签列表