1.前言
现如今各大电商,新闻媒体等app开发中少不了原生和H5混合开发。而这些在Android 端均是通过WebView实现Java和JavaScript的交互的。就拿最近遇到的现象来举一个栗子:
2. 交互方式概要:
两者之间的交互方式大概可以概括为以下两种方式
- Java去调用JS的代码
-
JS去调用Java的代码
对于Android调用JS代码的方法有2种:
- 通过WebView的loadUrl()
- 通过WebView的evaluateJavascript()
对于JS调用Android代码的方法有3种:
- 通过WebView的addJavascriptInterface() 进行对象映射
- 通过 WebViewClient 的shouldOverrideUrlLoading() 方法回调拦截 url
- 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截JS对话框alert()、confirm()、prompt() 消息。
3.整体机构
在assets目录下创建一个本地html文件如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>DoTry</title>
<script>
// Android需要调用的方法
function callJS(code){
alert("Android调用了JS的callJS方法"+code);
}
function getGreetings(code) {
return code;
}
//js调用Android,对象调用该方法
function callAndroid(){
test.nativeJavaMethod("js调用了android中的hello方法");
}
function callAndroidByUrl(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
function clickprompt(){
// 调用prompt()
var result=prompt("js://demo?arg1=111&arg2=222");
<!--alert("demo " + result);-->
}
</script>
</head>
js 调用java 模板
<body>
//点击按钮则调用callAndroid函数
<button type="button" id="button1" onclick="callAndroid()">js 调用java</button>
<button type="button" id="button2" onclick="callAndroidByUrl()">调用callAndroidByUrl</button>
<button type="button" id="button3" onclick="clickprompt()">点击调用Android代码</button>
</body>
</html>
对WebView做出对应的设置:
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
3.Java调用JavaScript
- 通过WebView的loadUrl()方法调用js:
在通过webview调用loadurl,可以传入参数:
case R.id.java_js:
//java 执行js 可以传入参数。
mWebView.loadUrl("javascript:callJS(1)");
break;
callJS方法是一个javascript弹框的方法,这个弹框效果在WebChromeClient 中可以接收到到
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
其实这里就已经存在java-js-java的一个调用流程。通过WebChromeClient 中的一些方法可以获取到相关方法,js可以传值到java。但是如果是普通js,不是弹框这种类型的就不好获取js的返回值了。
- 通过WebView的evaluateJavascript():
该方法较loadurl()方法有一个好处就是可以获取到返回值,调用方式如下:
case R.id.java_evaluateJavascript:
mWebView.evaluateJavascript("javascript:callJS(1)", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
Log.i(TAG, "s is ===" + s);
}
});
break;
可以看到log如下:因此evaluateJavascript可以更方便的获取返回值。
4.JavaScript调用Java
- 通过 WebView的addJavascriptInterface()进行对象映射。
定义一个与js映射关系的java类
public class AndroidtoJs extends Object{
// 定义JS需要调用的方法
// 被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void nativeJavaMethod(String msg) {//msg 由js传入
Log.i("MainActivity","JS调用了Android的nativeJavaMethod方法==="+msg);
}
}
在上面的html的js文件中做如下调用:
//js调用Android,对象调用该方法
function callAndroid(){
test.nativeJavaMethod("js调用了android中的nativeJavaMethod方法");
}
log系统如下:
可以看到js通过对象映射的方法会调用到java方法。
- 通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url
- Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
- 解析该 url 的协议
- 如果检测到是预先约定好的协议,就调用相应方法
在html文件中定义一个js方法,实现相关请求如下:
function callAndroidByUrl(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
在java代码中进行url解析拦截如下:
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
// 如果url的协议 = 预先约定的 js 协议
// 就解析往下解析参数
if (uri.getScheme().equals("js")) {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri.getAuthority().equals("webview")) {
// 步骤3:
// 执行JS所需要调用的逻辑
Log.i(TAG, "拦截到相关url,去做其他事宜。");
}
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
拦截到url后可以在java中做其他相关操作。这也就是一开始百度app采用这种方式做的原理。
- 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截JS对话框alert()、confirm()、prompt() 消息。
其实前面在loadurl的时候已经用到这种方法了,应为js的弹框等相关操作在WebChromeClient 中均可以捕获到,故此可以在接收到js相关方法后java中处理。
js代码如下:
function clickprompt(){
// 调用prompt()
var result=prompt("js://demo?arg1=111&arg2=222");
<!--alert("demo " + result);-->
在java中做如下执行即可
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
Uri uri = Uri.parse(url);
// 如果url的协议 = 预先约定的 js 协议
// 就解析往下解析参数
if (uri.getScheme().equals("js")) {
// 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议
// 所以拦截url,下面JS开始调用Android需要的方法
if (uri.getAuthority().equals("webview")) {
// 步骤3:
// 执行JS所需要调用的逻辑
Log.i(TAG, "拦截到相关url,去做其他事宜。");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
这就是js调用java代码的实现效果。
总结
关于java和js之间相互调用,可以简单归纳为以上集几种方法,市面上会有一些比较好的开源库可以借鉴,但其原理就是上面说的这几种。对以上方法作出如下总结:
以上是关于WebView的一些java同js交互的问题,当然java和js之间交互会存在一些其他问题,比如漏洞等相关问题,这些都是在开发过程中需要注意到的。
文中代码地址:WebViewSample