做为开发人员都知道,客户端的版本更新对于用户来讲代价是很大的。为了知足客户端可以快速更新迭代的要求,许多app都内嵌入了H5,好比不少电商平台,淘宝、京东、
聚划算等等。这类技术的关键就是在于Android客户与Web前端之间的交互。不少大型项目的接口为了防止Spammer的侵入,都是要求只能由客户端发起请求的。因此本项目
就封装了一个module,实现客户端接收前端的调用,而后由客户端发起Http请求的功能。
复制代码
开始介绍项目以前,先来快速回顾一下Android客户端与Web前端之间交互的几种方式。
复制代码
android客户端代码:javascript
private void initWebView() {
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true); // 设置与Js交互的权限
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 设置容许JS弹窗
webView.loadUrl("file:///android_asset/javascript.html");
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(SimpleWebViewActivity.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;
}
});
}
private void setListener() {
btnLoadUrl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
webView.post(new Runnable() {
@Override
public void run() {
// 此处的callJS方法名与JS中的function方法名必需要一致
webView.loadUrl("javascript:callJS()");
}
});
}
});
}
复制代码
javascript前端代码:html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
// JS代码
<script>
// Android须要调用的方法
function callJS() {
alert("Android调用了JS的callJS方法");
}
</script>
</head>
</html>
复制代码
运行结果如图
复制代码
btnEvaluateJavascript.setOnClickListener(new View.OnClickListener() {
@TargetApi(19)
@Override
public void onClick(final View v) {
webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
Log.d(TAG, "value---" + value);
}
});
}
});
复制代码
运行结果跟上图是同样的。
复制代码
两种交互方式的比较
复制代码
调用方式 | 优势 | 缺点 | 使用场景 |
---|---|---|---|
使用loadUrl() | 方便简洁 | 效率低;获取返回值麻烦 | 不须要使用返回值,对性能要求较低 |
使用evaluateJavascript() | 效率高 | 向下兼容性差(仅Android 4.4以上可用) | Android 4.4及以上 |
// Android版本变量
final int version = Build.VERSION.SDK_INT;
// 由于该方法在 Android 4.4 版本才可以使用,因此使用时需进行版本判断
if (version < 18) {
webView.loadUrl("javascript:callJS()");
} else {
webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
复制代码
代码再也不贴出,详细代码请参见:代码地址 运行结果以下: 前端
代码再也不贴出,详细代码请参见:代码地址 运行结果以下: java
代码再也不贴出,详细代码请参见:代码地址 运行结果以下: android
2.3.1 onJsAlert()、onJsConfirm()、onJsPrompt()三者之间的比较
方法 | 做用 | 返回值 |
---|---|---|
alert() | 弹出警告框 | 没有 |
confirm() | 弹出确认框 | 两个返回值(true或false) |
prompt() | 弹出输入框 | 任意设置返回值 |
2.3.2 总结
经常使用的拦截是:拦截 JS的输入框(即prompt()方法),由于只有prompt()能够返回任意类型的值,操做最全面方便、更加灵活,
而alert()对话框没有返回值,confirm()对话框只能返回两种状态(肯定 / 取消)两个值。
复制代码
调用方式 | 优势 | 缺点 | 使用场景 |
---|---|---|---|
经过WebView的addJavascriptInterface() 进行对象映射 |
方便简洁 | Android 4.2如下存在漏洞问题 | Android 4.2以上相对简单的互调场景 |
经过 WebViewClient 的方法shouldOverrideUrlLoading() 回调拦截 url |
不存在漏洞问题 | 有协议约束,客户端向前端传值繁琐 | 不须要返回值的互调场景 |
经过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法 回调拦截JS对话框alert()、confirm()、prompt()消息 |
不存在漏洞问题 | 有协议约束 | 能知足大多数状况下的互调场景 |
附:WebView的addJavascriptInterface()方法在Android 4.2如下存在的漏洞git
先来看一下项目运行的效果图:
复制代码
你们会说,这个不是跟拦截JS的prompt()方法同样么,没错,js和android之间的交互,也无非上面提到的几种方法,这里作的封装采用的是:
js调android:经过WebView的addJavascriptInterface()进行对象映射
android回调js:android 4.4以上采用WebView的evaluateJavascript()方法,android 4.4如下采用loadUrl()方法
复制代码
android端代码:
复制代码
JsBridgeWebView 这是一个继承WebView的类,它里面向JS注入了一个对象供JS调用,JS能够经过这个对象调用Native的方法,调哪一个方法,传哪些参数,彻底由JS决定,方法名必须两端协议,Native经过反射找到对应的方法。传递过来的参数重包含了JS回调方法的方法名,客户端执行完相应的操做以后再去执行JS的方法。github
js代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
// 因为对象映射,因此调用jsCallback对象等于调用Android映射的对象
var json = "{\"name\": \"zlove\", \"_dscbstub\": \"callback\"}"
_jsbridge.call("testAsync", json);
}
function callback(result) {
alert("客户端返回的结果是:" + result)
}
</script>
</head>
<body>
<!-- 点击按钮则调用callAndroid函数 -->
<button type="button" id="button1" style="font-size:30px" onclick="callAndroid()">Call Android</button>
</body>
</html>
复制代码
var json = "{\"name\": \"zlove\", \"_dscbstub\": \"callback\"}"
_jsbridge.call("testAsync", json);
_jsbridge表示客户端注入的对象,testAsync是方法名,json是参数,而_dscbstub对应的callback是js的回调方法。
复制代码
原本觉得要写不少,事实上其实把基础原理写清楚了,也就这么多😂😂😂。
I hope this will help you!
复制代码
附:源码地址web