一、Js调用Java,Java调用Js
在Android开发中,能实现Js调用Java,有4种方法:
1.JavascriptInterface
2.WebViewClient.shouldOverrideUrlLoading()
3.WebChromeClient.onConsoleMessage()
4.WebChromeClient.onJsPrompt()
1.1 JavascriptInterface
这是Android提供的Js与Native通信的官方解决方案。
首先Java代码要实现这么一个类,它的作用是提供给Js调用。
public class JavascriptInterface {
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
}
}
然后把这个类添加到WebView的JavascriptInterface中。webView.addJavascriptInterface(new JavascriptInterface(), “javascriptInterface”); 在Js代码中就能直接通过“javascriptInterface”直接调用了该Native的类的方法。
function showToast(toast) {
javascript:javascriptInterface.showToast(toast);
}
但是这个官方提供的解决方案在Android4.2之前存在严重的安全漏洞。在Android4.2之后,加入了@JavascriptInterface才得到解决。所以考虑到兼容低版本的系统,JavascriptInterface并不适合。
1.2 WebViewClient.shouldOverrideUrlLoading()
这个方法的作用是拦截所有WebView的Url跳转。页面可以构造一个特殊格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native就能执行自身的逻辑了。
public class CustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isJsBridgeUrl(url)) {
// JSbridge的处理逻辑
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
public class CustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isJsBridgeUrl(url)) {
// JSbridge的处理逻辑
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
1.3 WebChromeClient.onConsoleMessage()
这是Android提供给Js调试在Native代码里面打印日志信息的API,同时这也成了其中一种Js与Native代码通信的方法。在Js代码中调用console.log(‘xxx’)方法。
console.log('log message that is going to native code')
就会在Native代码的WebChromeClient.consoleMessage()中得到回调。consoleMessage.message()获得的正是Js代码console.log(‘xxx’)的内容。
public class CustomWebChromeClient extends WebChromeClient {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
super.onConsoleMessage(consoleMessage);
String msg = consoleMessage.message();//JavaScript输入的Log内容
}
}
public class CustomWebChromeClient extends WebChromeClient {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
super.onConsoleMessage(consoleMessage);
String msg = consoleMessage.message();//JavaScript输入的Log内容
}
}
1.4 WebChromeClient.onJsPrompt()
其实除了WebChromeClient.onJsPrompt(),还有WebChromeClient.onJsAlert()和WebChromeClient.onJsConfirm()。顾名思义,这三个Js给Native代码的回调接口的作用分别是展示提示信息,展示警告信息和展示确认信息。鉴于,alert和confirm在Js的使用率很高,所以JSBridge的解决方案中都倾向于选用onJsPrompt()。
Js中调用
window.prompt(message, value)
WebChromeClient.onJsPrompt()就会受到回调。onJsPrompt()方法的message参数的值正是Js的方法window.prompt()的message的值。
public class CustomWebChromeClient extends WebChromeClient {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 处理JS 的调用逻辑
result.confirm();
return true;
}
}
public class CustomWebChromeClient extends WebChromeClient {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 处理JS 的调用逻辑
result.confirm();
return true;
}
}
1.5 Java调用Js
前文提到的4种通信方式都是Js通信Native的Java,而反过来,Java通信Js只有一种方式。那就是调用WebView.loadUrl()去执行一个预先定义好的Js方法。
webView.loadUrl(String.format("javascript:WebViewJavascriptBridge._handleMessageFromNative(%s)", data));
思路
首先先说思路,有经验的同学可能都知道Android的WebView中有一个WebChromeClient类,这个类其实就是用来监听一些WebView中的事件的,我们发现其中有三个这样的方法。
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
好了,说到这里我们前期的准备工作也就做好了,其实就是通过重写WebView中WebChromeClient类的onJsPrompt()方法来进行js和java的通信。这三个方法其实就对应于js中的alert(警告框),comfirm(确认框)和prompt(提示框)方法,那这三个方法有什么用呢?前面我们说了JSBridge的作用是提供一种js和java通信的框架,其实我们可以利用这三个方法去完成这样的事。比如我们可以在js脚本中调用alert方法,这样对应的就会走到WebChromeClient类的onJsAlert()方法中,我们就可以拿到其中的信息去解析,并且做java层的事情。那是不是这三个方法随便选一个就可以呢?其实不是的,因为我们知道,在js中,alert和confirm的使用概率还是很高的,特别是alert,所以我们最好不要使用这两个通道,以免出现不必要的问题。
有了实现方案,下面就是一些具体的细节了,大家有没有想过,怎么样才能让java层知道js脚本需要调用的哪一个方法呢?怎么把js脚本的参数传递进来呢?同步异步的方式又该怎么实现呢?下面提供一种我的思路。
首先大家都知道http是什么,其实我们的JSBridge也可以效仿一下http,定义一个自己的协议。比如规定sheme,path等等。下面来看一下一些的具体内容:
hybrid://JSBridge:1538351/method?{“message”:”msg”}
是不是和http协议有一点像,其实我们可以通过js脚本把这段协议文本传递到onPropmt()方法中并且进行解析。比如,sheme是hyrid://开头的就表示是一个hybrid方法,需要进行解析。后面的method表示方法名,message表示传递的参数等等。
有了这样一套协议,我们就可以去进行我们的通信了。
代码
先看一下我们html和js的代码
有前端经验的同学应该能很轻松的看懂这样的代码,对于看不懂的同学我来解释一下,首先看界面。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="file:///android_asset/jsBridge.js" type="text/javascript"></script>
</head>
<body>
<div class="blog-header">
<h3>JSBridge</h3>
</div>
<ul class="entry">
<br/>
<li>
toast展示<br/>
<button οnclick="JsBridge.call('JSBridge','toast',{'message':'我是气泡','isShowLong':0},function(res){});">toast</button>
</li>
<br/>
<li>
异步任务<br/>
<button οnclick="JsBridge.call('JSBridge','plus',{'data':1},function(res){console.log(JSON.stringify(res))});">plus</button>
</li>
<br/>
<br/>
</ul>
</body>
</html>
(function (win, lib) {
var doc = win.document;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var JsBridge = win.JsBridge || (win.JsBridge = {});
var inc = 1;
var LOCAL_PROTOCOL = 'hybrid';
var CB_PROTOCOL = 'cb_hybrid';
var CALLBACK_PREFIX = 'callback_';
//核心功能,对外暴露
var Core = {
call: function (obj, method, params, callback, timeout) {
var sid;
if (typeof callback !== 'function') {
callback = null;
}
sid = Private.getSid();
Private.registerCall(sid, callback);
Private.call