前言
上篇介绍了WebView的基本使用,WebView使用中常用的类和方法。本篇将介绍WebView中Android原生Js之间交互。以及它们之间通信桥梁JsBridge。
1.Android调用JS
Android调用JS有两种方法,第一种是通过loadUrl()方法,第二种是通过evaluateJavascript()。
1-1.loadUrl()示例
首先新建index.html放在android工程的assets目录下。
看下Html中的JS代码和Android中调用,JS代码相对比较简单。
<html>
<head>
<meta charset="utf-8" />
<title>WebView测试title>
head>
<script type="text/javascript">
function androidCallJs(){
alert("android 调用了 JS了")
}
script>
html>
看下Android中如何实现调用JavaScript代码的。
布局文件:
id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Kotlin代码:
/*初始化webview*/
val webView = getView(R.id.webview)
/*设置webview选项参数*/
val webSetting = webView!!.settings
/*设置webview可以调用javascript代码*/
webSetting.javaScriptEnabled = true
/*设置javascript可以自动弹弹窗*/
webSetting.javaScriptCanOpenWindowsAutomatically = true
/*加载html,这里android_asset是固定写法*/
webView!!.loadUrl("file:///android_asset/index.html")
callJsBtn = getView(R.id.callJsBtn)
callJsBtn!!.setOnClickListener {
/*点击调用Js方法*/
webView!!.loadUrl("javascript:androidCallJs()")
}
/*webview是加载整个html作用,内容的渲染需要使用它的功能辅助类WebviewChromClient类去实现*/
webView!!.webChromeClient = object : WebChromeClient() {
override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
val b = AlertDialog.Builder(this@MainActivity);
b.setTitle("Android Call Js")
b.setMessage(message)
b.setPositiveButton("确定",object :DialogInterface.OnClickListener{
override fun onClick(dialog: DialogInterface?, which: Int) {
result!!.confirm()
}
})
b.setCancelable(false)
b.create().show()
return true
}
}
1-2.evaluateJavascript()示例
<html>
<head>
<meta charset="utf-8" />
<title>WebView测试title>
head>
<script type="text/javascript">
function androidCallJs(){
alert("android evaluateJs 调用了 JS了")
return "Android你能收到消息吗?"
}
script>
html>
callJsBtn!!.setOnClickListener {
/*第二种方法调用JS*/
webView!!.evaluateJavascript("javascript:androidCallJs()", object : ValueCallback<String> {
/*Js返回的值*/
override fun onReceiveValue(value: String?) {
ToastUtils.show(value!!)
}
})
// webView!!.loadUrl("javascript:androidCallJs()")
}
只要将第一种方法loadUrl()更换为evaluateJavascript()方法即可。这种方法相比较第一种方法,能够接收到JS返回值,loadUrl()只能显示。
2.JS调用Android
2-1.WebView提供方法 addJavascriptInterface() 将Js和Android建立映射关系。
2-1-1.Html声明映射对象以及Android被调用的方法
<html>
<head>
<meta charset="utf-8" />
<title>JS调用Android测试1title>
head>
<script type="text/javascript">
function JsCallAndroid(){
testHello.helloJs("JsCallAndroid")
}
script>
<body>
<button type="button" id="btn" onclick="JsCallAndroid()">点击调用Android代码button>
body>
html>
2-1-2.定义与Js映射关系的Android类
/*
* 创建者: Ho
* 创建时间: 2018/6/6 on 19:46
* 描述: Js映射的Android类
* 其他说明: 无
*/
class AndroidToJs : Object() {
//注解映射
@JavascriptInterface
fun helloJs(msg: String) {
ToastUtils.show("Js 调用了 Android helloJs方法了")
}
}
2-1-3.Kotlin添加Js和Android映射关系
/*初始化webview*/
webView = getView(R.id.webview)
/*设置webview选项参数*/
val webSetting = webView!!.settings
/*设置webview可以调用javascript代码*/
webSetting.javaScriptEnabled = true
/*加载html*/
webView!!.loadUrl("file:///android_asset/index.html")
/*Webview添加Js和Android映射关系*/
webView!!.addJavascriptInterface(AndroidToJs(), "testHello")
AndroidToJs()是Android的映射类。Kotiln的写法。“testHello”是映射到JS的映射类的对象,名字可以随意取。
2-2. WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截消息解析。
<html>
<head>
<meta charset="utf-8">
<title>JS调用Android测试1title>
head>
<script>
function clickprompt(){
// 调用prompt() 定义js和android通信协议
var result=prompt("js://test?param1=hello11¶m1=hello22");
alert("test" + result);
}
script>
<body>
<button type="button" id="btn" onclick="clickprompt()">点击调用Android代码button>
body>
html>
webView!!.webChromeClient = object : WebChromeClient() {
override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?): Boolean {
val uri = Uri.parse(message)
// 如果uri协议 = 预先约定的 js 协议,解析参数
if (uri.scheme == "js") {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri.authority == "webview") {
// 执行JS所需要调用的逻辑
ToastUtils.show("Js 调用了 Android 中 helloToJS代码")
// 可以在协议上带有参数并传递到Android上
val params = HashMap<String, String>()
var collection = uri!!.queryParameterNames
//参数result:代表消息框的返回值(输入值)
result!!.confirm("js调用了Android的方法成功啦")
}
return true
}
return super.onJsPrompt(view, url, message, defaultValue, result)
}
}
WebView在Android4.2以下版本产生安全问题有一种情况就是通过第一种添加Js和Android映射关系的方式。第二种方式就是较为安全,不会产生安全漏洞问题。但是要Android和Js约定一个协议通信,相对实现起来比较复杂,onJsAlert()和onJsConfirm()和上述实现方式一样。
3.BridgeWebView(Hybrid)
BridgeWebView,继承了WebView类,实现了WebViewJavascriptBridge接口。各位大佬一看就知道是一个Js和Android通信的封装类。这里介绍下它的使用方式。
3-1.Gradle引入
compile 'com.github.lzyzsd:jsbridge:1.0.4'
3-2.使用方法
3-2-1.自定义JS事件回调类
private class MyHadlerCallBack extends DefaultHandler {
@Override
public void handler(String data, CallBackFunction function) {
if (function != null) {
Toast.makeText(JsBridgeVoteActivity.this, "自定义类继承DefaultHandler:" + data, Toast.LENGTH_SHORT).show();
}
}
}
3-2-2.自定义JS事件回调类
/*创建实例*/
BridgeWebView webView = new BridgeWebView(this);
/*设置默认回调处理类*/
webView.setDefaultHandler(new MyHadlerCallBack());
/*注册Js和android之间通信的事件*/
webView.registerHandler("openLogin", (data, function) -> openLogin());
以上面的代码为例,在Android中注册了事件 “openLogin”,这里也需要在Html页面的Js代码中进行注册相同的事件名称 “openLogin”。意味着在Html页面上用户(未登录情况下)触发登录操作。登录事件要在boolean isBindPhone代码中完成。
private void openLogin() {
new LoginDialog(this, new LoginDialog.OnLoginCallBack() {
@Override
public void login() {
HashMap params = new HashMap<>();
/*method是和js约定,这是调用方法,后面是登录成功操作*/
params.put("method", "loginSuccess");
/*webView通知js已经登录成功*/
webView.callHandler("call", new Gson().toJson(params), data ->{
});
}
}).show();
}
Js调用Android登录方法,Android端登录成功后通过webView.callHandler方法通知H5页面(Js)客户端登录成功,做相应的操作即可。BridgeWebView的使用方法就是这么简单,内部已经将通信机制封装好。
结尾
本篇介绍了WebView上Js和Android互相调用方法。在大前端概念的趋势下,原生Android和H5(JS)的联系会越来越紧密。下篇将介绍在重构过程中WebView上遇到的问题,以及WebView最让人关系的内存泄漏问题以及解决方案。对于H5和Java层结合的混合开发Hybrid的研究也将在以后的WebView系列中做介绍。
Android WebView系列(一)WebView的基本使用