做Hybird APP开发的同学,应该对JSBridge不陌生,它用于H5页面和Native(Android或者iOS)通信。常用的三方库如Dsbridge系列(https://github.com/wendux/DSBridge-Android)。那么,你知道JSBridge到底是如何在两端进行通信的吗?
下面的实例代码,Native端以Android为例。
Native应用可以在移动端系统中注册一个Schema协议的URI,这个URI可以在系统的任意地方授权访问,用来调起一段原生方法,或者唤起一个原生界面。
于是,Native WebView控件中的H5页面,可以通过JS代码请求这个通用Schema协议。
比如,通过添加一个不可见的iframe,设置其src
属性,发送一个URI请求。
let iframe = document.createElement('iframe');
iframe.setAttribute('style', 'display:none');
document.body.appendChild(iframe);
iframe.setAttribute('src', 'myapp://className/method?args')
整个调用流程如下图。一旦系统捕获到注册表中的Schema URI,就会通过此URI地址执行该Schema协议定义的Native操作,执行一段Native代码或者打开APP的某个页面(如打开摄像头,唤起图片预览功能,跳转APP支付页面等)
jsBridge_native Schema.png
以Android为例,可以通过addJavascriptInterface
方法将Native的一个对象注入到页面中,供JS调用。
/**
* 添加javascriptInterface
* 第一个参数:这里需要一个与js映射的java对象
* 第二个参数:该java对象被映射为js对象后在js里面的对象名,在js中要调用该对象的方法就是通过这个来调用
*/
webView.addJavascriptInterface(new JSInterface(), "android");
private final class JSInterface{
/**
* 注意这里的@JavascriptInterface注解, target是4.2以上都需要添加这个注解,否则无法调用
* @param text
*/
@JavascriptInterface
public void showToast(String text){
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
}
--------------------------------------------------------------------
/**
* js中调用java方法
*/
android.showToast('toast');
Native会向webView全局作用域注入一个android
的全局对象,该对象上有showToast
的方法。
Android 4.2 之前注入对象的接口是 addJavascriptInterface
,但是由于安全原因慢慢不被使用(4.2以下版本,通过JS可以访问设备SD卡上面的任何内容,甚至是联系人信息,短信等。此为安全漏洞)。
现在,一般会通过拦截JS原生的window.confirm
或window.prompt
方法,从而达到H5向Native通信的目的。
如,在 Webview 上添加 onJsConfirm
或onJsPrompt
监听(其实,监听window.console
或者window.alert
也是可以的,但是这两个方法在JS coding中比较常用,所以为了避免不必要的事件触发,一般我们不会选择在客户端劫持它们)。代码如下:
webview.setWebChromeClient(new WebChromeClient());
public class JSBridgeWebChromeClient extends WebChromeClient {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message, defaultValue);
......
return true;
}
}
反之,如果Native需要主动调用JS的方法,又该怎么做呢?
很简单,只要 H5 将 JS 方法暴露在 Window 上给 Native 调用即可。 是不是非常像客户端注册 Schema URI呢?
JS注册好函数,Native就可以调用了。
Android 4.4 以前,通过 loadUrl 方法,执行一段 JS 代码来实现(缺点是效率低,无法获得返回结果,且调用的时候会刷新 WebView):
/**
* js中声明全局函数
*/
<script>
function log(msg) {
console.log(msg);
}
</script>
--------------------------------------------------------------------
/**
* 设置与Js交互的权限
*/
webSettings.setJavaScriptEnabled(true);
private final class JSInterface{
@JavascriptInterface
public void showJsLog(String text){
webView.loadUrl("javascript:log('"+text+"')");
}
}
4.4 以后,可以使用 evaluateJavascript
方法实现(效率更高,可获取返回值,调用时候不刷新WebView)
String text = "hello world";
webView.evaluateJavascript("javascript:log('"+text+"')", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value){
......
}
});
Native和H5需要在接口设计上达成一致。只要API规定好,后续通信实现就不难了。