JSBridge

971 阅读4分钟

简介

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

实际上,JSBridge 就像其名称中的Bridge的意义一样,是 Native 和非 Native 之间的桥梁,它的核心是构建 Native 和非 Native 间消息通信的通道,而且这个通信的通道是双向的。

双向通信的通道:

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

交互图:

javascript执行以下四种行为会被webview监听到,箭头后面是对应触发的Java方法。

1、window.alert => onJSAlert
2、window.confirm => onJSConfirm
3、window.prompt => onJsPrompt
4、window.location => shouldOverrideUrlLoading

Android

JS向原生发消息 JavaScript 调用 Android 方法

第一种方法:弹窗的拦截

alert
confirm
prompt

举个例子:页面播放视频前判断手机使用的网络环境是WI-FI还是蜂窝网

原生应用拦截弹窗返回消息,这是同步的

异步怎么做?

告诉原生代码,不需要立即知道结果,有了结果后回调方法就可以。

原生代码如何执行JS告诉的回调方法?

定义回调方法,并请求

原生代码:在web页面注入js语句,获得window对象,调用回调方法(原生应用注入到JS中的代码)

第二种方法:URL拦截

通过schema方式,使用shouldOverrideUrlLoading方法对url协议进行解析。这种js的调用方式与ios的一样,使用iframe来调用native代码

URL请求被原生代码截获,去传输数据

对URL对定制:这是JS代码给我传递的消息,而不是真的页面跳转

协议、干什么、哪个回调方法

Native拦截jsbridge开头的网络请求,做出对应的动作。

第三种方法:JSInterface

就是在页面的 window 对象里注入了 AndroidJS 对象。在js里可以直接调用

class JSInterface {
    @JavascriptInterface //注意这个代码一定要加上
    public String getUserData() {
        return "UserData";
    }
}
webView.addJavascriptInterface(new JSInterface(), "AndroidJS");

Native调用javascript方式

在android里是使用webview的loadUrl进行调用的,如:

// 调用js中的JSBridge.trigger方法
webView.loadUrl("javascript:JSBridge.trigger('webviewReady')");

IOS

IOS 调用 JavaScript 方法

Native调用Javascript语言,是通过UIWebView组件的stringByEvaluatingJavaScriptFromString方法来实现的,该方法返回js脚本的执行结果。

在 IOS 中使用 stringByEvaluatingJavaScriptFromString 方法直接调用挂载在前端 window 下的方法,并获取方法返回的数据。

webview.stringByEvaluatingJavaScriptFromString("Math.random()")

从上面代码可以看出它其实就是调用了window下的一个对象,如果我们要让native来调用我们js写的方法,那这个方法就要在window下能访问到。但从全局考虑,我们只要暴露一个对象如JSBridge对native调用就好了,所以在这里可以对native的代码做一个简单的封装:

//下面为伪代码
webview.setDataToJs(somedata);
webview.setDataToJs = function(data) {
 webview.stringByEvaluatingJavaScriptFromString("JSBridge.trigger(event, data)")
}

JavaScript 调用 IOS 方法

反过来,Javascript调用Native,并没有现成的API可以直接拿来用,而是需要间接地通过一些方法来实现。UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView内发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1&param2=value2

于是在UIWebView的delegate函数中,我们只要发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑。

发起这样一个网络请求有两种方式:1. 通过localtion.href;2. 通过iframe方式; 通过location.href有个问题,就是如果我们连续多次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。

使用iframe方式,以唤起Native APP的分享组件为例,简单如下:

在 JavaScript 中使用创建一个 iframe 请求 URL 的方式将带有 scheme 的 URL 传给 IOS

var url = 'jsbridge://doAction?title=分享标题&desc=分享描述&link=http%3A%2F%2Fwww.baidu.com';
var iframe = document.createElement('iframe');
iframe.style.width = '1px';
iframe.style.height = '1px';
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
setTimeout(function() {
    iframe.remove();
}, 100);

IOS 在 WebView 获取到网页 URL 请求时将 scheme 码的 URL 拦截下来去做 native 方法的处理

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
        print("shouldStartLoadWithRequest")
        let url = request.URL
        let scheme = url?.scheme
        let method = url?.host
        let query = url?.query
        
        if url != nil && scheme == "jsbridge" {
            print("scheme == \(scheme)")
            print("method == \(method)")
            print("query == \(query)")

            switch method! {
                case "getData":
                    self.getData()
                case "putData":
                    self.putData()
                case "gotoWebview":
                    self.gotoWebview()
                case "gotoNative":
                    self.gotoNative()
                case "doAction":
                    self.doAction()
                case "configNative":
                    self.configNative()
                default:
                    print("default")
            }
    
            return false
        } else {
            return true
        }
    }

项目通用方法抽象

在项目的实践中,我们逐渐抽象出一些通用的方法,这些方法基本上都是可以满足项目的需求。如下所示:

1.getData(datatype, callback, extra) H5从Native APP获取数据

使用场景:H5需要从Native APP获取某些数据的时候,可以调用这个方法。

示例代码:

JSBridge.getData('userInfo',function(data) {
    console.log(data);
});