在app中,经常会遇到一些活动推广的页面,大多数活动具备时效性强、运营时间短的特征,这些活动一般都是通过H5页面快速投放到产品的活动模块,来和用户进行交互。如何建立web页面和本地Native页面的深度交互,这就接下来要重点介绍的JSBridge,先看一个效果图:图一JS调用Android,图二Android调用JS。
Android调用JS
在android为我们提供了WebView,他有三个方法loadUrl,LoadData,LoadDataWithBase
(1)loadUrl:这个url可以是一个远程的网络路径,也可以是一个本地的uri地址。
(2)LoadData:是LoadUri的增强版,可以指定编码格式,不至于造成乱码
(3)LoadDataWithBase:可以指定资源路径,例如一个网页可能包含image,css,js文件夹,这时候如果使用loadUrl会导致图片资源无法加载,布局错乱,这时候需要使用这个方法指定资源路径。
通过以上三种方式android就可以和web进行通信了,同时android也为我们提供了addJavascriptInterface,该方法负责把Object 对象暴露成 JavaScript中的name对象。WebView存在一个漏洞,该漏洞已经在Android 4.2上修复了,即使用@JavascriptInterface代替addJavascriptInterface。另外一个问题是在Android4.4版本之前WebView使用的是Webkit,在之后的版本中采用Chromium浏览器内核标准,由于以上安全性和兼容性问题,基本上不会使用系统原生的一些方法,这里可以使用JSBridge来进行web端和android端的数据通讯。
JS调用Android
(1)webView.addJavascriptInterface()
(2)WebViewClient.shouldOverrideUrlLoading()
(3)WebChromeClient.onJsAlert()/onJsConfirm()/onJsPrompt() 方法分别回调拦截JS对话框alert()、confirm()、prompt()消息。
JSBridge与Android通信原理
JSBridge是一座用JavaScript搭建起来的桥,替代了WebView的自带的JavascriptInterface的接口,使得我们的开发更加灵活和安全。一端是web,一端是native,他可以根据web和native约定好的规则来通知native要做什么,从而实现Android和Javascript之间的交互。
在Android调用JS我们可以使用WebView.loadUrl(“javascript:function()”)进行加载,但是当H5调用Android时却不是那么方便了。先来看看JSBridge与Android之间的通信原理:
在WebView中,有一个setWebChromeClient方法,可设置WebChromeClient对象,而这个对象中有三个方法,分别是onJsAlert,onJsConfirm,onJsPrompt,当js调用window对象的对应的方法,即window.alert,window.confirm,window.prompt,WebChromeClient对象中的三个方法对应的就会被触发,这时候WebView的shouldOverrideUrlLoading根据传输协议就会拦截到消息,就可以在这些方法里面进行Android中方法的调用。基于此原理,JSBridge制定了一个通信协议,类似于http协议中url传输协议,来看看统一资源标识符URI组:http://host:port/path?param=value,在JSBridge中的也是类似于这种协议,如下:
jsbridge://className:port/methodName?jsonObj
(1)className:Android端实现暴露给前端的类名;
(2)port:Android返回结果给前端的端口;
(3)methodName:前端需要调用的函数 ;
网页端给Android传递的参数,这里传递的是一个json对象,当H5页面调用进行操作时,通过JSBridge出发一个uri scheme,通过scheme传递一些参数数据,Android捕获到这些消息后会根据scheme中的信息调用相应的方法,执行完毕后调用JSBridge对象回调方法,并且传入结果和id,最后H5再回调此结果,得到反馈。如下图:
1、Android调用通过loadUrl(url)调用JS对象,可以在URL内传递参数。
2、JS调用Android是通过shouldOverrideUrlLoading拦截uri。
3、JsBridge将数据封装成Message,然后放进Queue,再将Queue通过协议进行传输。
Android端使用
1.引入库文件
在repositories 下引入:maven { url "https://jitpack.io" }
在dependencies引入: compile 'com.github.lzyzsd:jsbridge:1.0.4‘2.初始化设置
final BridgeWebView bridgeWebView = (BridgeWebView) findViewById(R.id.JsBridgeWebView);
bridgeWebView.setDefaultHandler(new DefaultHandler());
bridgeWebView.setWebChromeClient(new WebChromeClient());
bridgeWebView.loadUrl("file:///android_asset/a.html");
3.注册回调
/**
* js调用Android
*
* 参数一:getUserInfo就是注册供JS调用的方法名,
* 参数二:data是JS传过来的参数,
* 参数三:CallBackFunction 函数中需要把JS需要的response返回给JS
*/
bridgeWebView.registerHandler("submitFromWeb", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
Log.e("TAG", "js返回:" + data);
//显示js传递给Android的消息
Toast.makeText(MainActivity.this, "js返回:" + data, Toast.LENGTH_LONG).show();
//Android返回给JS的消息
function.onCallBack("我是js调用Android返回数据:" + etText.getText().toString());
}
});
如上图一演示情况,打印日志如下:
/**
* Android调用js
*
* 参数一:js中的方法名称
* 参数二:Android传递给js数据
* 参数三:回调接口,data为Android调用js方法的返回数据
*/
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bridgeWebView.callHandler("functionInJs", "Android调用js的方法", new CallBackFunction() {
@Override
public void onCallBack(String data) {
Log.e("TAG", "onCallBack:" + data);
Toast.makeText(MainActivity.this, data, Toast.LENGTH_LONG).show();
}
});
}
});
如上图二演示情况,打印日志如下:
注意:以上回调用法的传递名submitFromWeb、functionInJs必须与js保持一致。
网页端使用
以下为web端使用示例,备注标明的很详细:
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<title>js调用java</title>
</head>
<body>
<p>
<input type="text" id="text1" value="请输入测试数据" width="400px" height="200px"/>
</p>
<p>
<input type="button" id="enter" value="调用安卓的方法" οnclick="testClick();"
/>
</p>
<script>
//js调用Android方法:接收Android传递过来的数据,并做处理
function testClick() {
//参数一:调用java中的方法 submitFromWeb是方法名,必须和Android中注册时候的方法名称保持一致
//参数二:返回给Android端的数据,可以为字符串,json等信息
//参数三:js接收到Android传递过来的数据之后的相应处理逻辑
window.WebViewJavascriptBridge.callHandler(
'submitFromWeb'
, {'param': "JS成功接收到数据---"}
, function(responseData) {
alert(responseData)
}
);
}
//JS注册事件监听
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function() {
callback(WebViewJavascriptBridge)
},
false
);
}
}
//注册回调函数,第一次连接时调用 初始化函数
connectWebViewJavascriptBridge(function(bridge) {
//初始化
bridge.init(function(message, responseCallback) {
var data = {
'Javascript Responds': 'Wee!'
};
responseCallback(data);
});
//Android调用js方法:functionInJs方法名称需要保持一致 ,并返回给Android通知
bridge.registerHandler("functionInJs", function(data, responseCallback) {
alert(data);
var data = document.getElementById("text1").value;
var responseData = "我是Android调用js方法返回的数据---"+ data;
responseCallback(responseData);
});
})
</script>
</body>
</html>
Android端、网页端代码如上,都有详细的备注,理解起来应该不难,最后附上源码,如对你有帮助送上一颗星吧:
https://github.com/yoonerloop/AndroidJSBridge点击打开链接