来源:https://75team.com/post/android-webview-and-js.html
客户端调用 JS
loadUrl()
客户端可以直接访问 javascript:console.log('hello') 这样的伪 URL 即可实现在页面注入需要执行的 JS 代码。调用方法如下:
Webview webview = (WebView) findViewById(R.id.webView);
webView.loadUrl("javascript:console.log('hello')");
这样就实现了调用 JS 的目的了。
JS 调用客户端
JS 调用客户端的方法就比较多,可以分为注入映射和方法劫持两种。注入映射主要是使用官方提供的 addJavascriptInterface() 方法将 Java 对象和 JS 对象进行映射。方法劫持则是利用 JS 的一些系统方法调用会存在 Java 的事件回调,然后在回调中进行事件劫持,从而执行客户端方法。
addJavascriptInterface
addJavascriptInterface() 方法的使用非常简单,定义好被调用的方法对象后直接配置映射关系即可。
//定义好 Java 接口对象
public class SDK extends Object {
@JavascriptInterface
public void hello(String msg) {
System.out.println("Hello World");
}
}
//Webview 中调用
WebView webview = (WebView) findViewById(R.id.webview);
webview.addJavascriptInterface(new SDK(), 'sdk');
webview.loadUrl('http://imnerd.org'); //注入后加载页面
这样加载的页面中就可以直接执行 sdk.hello() 方法来执行客户端方法了。不过这种官方推荐的方法在 4.2及其一下系统上存在远程执行安全漏洞,对 4.2及其以下系统版本有要求的应用需要谨慎使用。
URL劫持
URL劫持主要是使用 shouldOverrideUrlLoading() 进行 Webview URL 劫持。从方法名可以看出,它是 Webview 拦截 URL 的一种回调,当 Webview 发生 URL 跳转的时候会触发该回调。在该回调中我们能够获取到前端提供的 URL 地址。我们通过构造约定协议的 URL 地址提供给客户端识别,识别成功后执行对应的方法即可。
WebView webview = (WebView) findViewById(R.id.webview);
webview.loadUrl('http://imnerd.org');
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.equals('sdk:hello')) {
System.out.println('hello world');
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
方法劫持
同 URL 劫持类似,方法劫持主要是利用 JS 的一些方法执行时会触发 Android 客户端中的一些回调,通过对前端参数进行识别来执行对应的客户端代码。目前前端主要有以下四种方法会触发对应的回调方法,对应关系如下:
JS方法 客户端回调
alert onJsAlert
prompt onJsPrompt
confirm onJsConfirm
console.log onConsoleMessage
将这四个方法列在一块是因为这几个方法的本质上都是差不多,定义好对应的回调方法即可。客户端具体的配置如下:
//定义好劫持回调类
private class hijackWebChromeClient extends WebChromeClient {
public boolean hijack(String text) {
if(text.equals('sdk:hello')) {
System.out.println('hello world');
return true;
}
return false;
}
@Override
public boolean onJsPrompt(WebView view, String message, String defaultValue, JSPromptResult result) {
if(this.hijack(message)) {
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if(this.hijack(message)) {
return true;
}
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
if(this.hijack(message)) {
return true;
}
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
String message = consoleMessage.message();
if(this.hijack(message)) {
return true;
}
return super.onConsoleMessage(consoleMessage);
}
@Override
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
if(this.hijack(message)) {
return true;
}
super.onConsoleMessage(message, lineNumber, sourceID);
}
}
//注入劫持回调类
WebView webview = (WebView) findViewById(R.id.webview);
webview.loadUrl('http://imnerd.org');
webview.setWebChromeClient(new hijackChromeClient);
这里为了方便展示,将所有回调的方法都写全了,实际上在实际的使用过程中一般都是约定好一种调用方式即可。另外 console.log 对应的回调写了两种,三参数的是老版本方法,在新API中已经被废弃,推荐使用 ConsoleMessage 对象传参方式。
说明
基本上交互的基本方式就是以上几种,不过有人将通信机制进行了封装,形成一套完善的 WebviewJSBridge 方案,提供了客户端调前端,前端调用客户端的系统解决方案。例如 lzyzsd/JsBridge: https://github.com/lzyzsd/JsBridge 项目,我们从代码中可以看到,其实它在底层是使用了 URL 劫持的方法与 JS 进行交互。
另外,微店也开源了一个小程序运行框架Hera: https://github.com/weidian-inc/hera,使用的是addJavascriptInterface方式进行的交互。
web app项目Cordova-Android: https://github.com/apache/cordova-android,是通过addJavascriptInterface(Android Webview的API)和JS Prompt这两种方式来实现JS对于Native API的调用。
https://github.com/wendux/DSBridge-Android