JSBridge实现原理探索:以toast为例
youngwind opened this issue · 1 comments
问题
我们知道,如果想要满足飞快增长的业务需要,必然得在APP中嵌入Web内容。一般来说,是java调用webview类新建一个webview,然后通过loadUrl加载Web页面。如果这个页面是纯展示性的还好说,对于复杂的页面,常常需要调用APP原生的功能,这个时候应该怎么办呢?
我们把问题抽象一下:“如何实现javascript和java的相互调用?”
把问题一分为二,先来看**“如何实现javascript调用java?”**
要实现的效果
我们要实现的效果如下图所示,点击某个按钮,打开一个新页面,然后加载web页面。点击web页面当中的按钮,调用安卓原生的方法toast,弹出提示。
如果你想在手机上查看真实效果的话,可以扫描下面的二维码安装测试包。
思路分析
我们来想想如何实现上面的功能。难点有这么几个:
- 如何创建一个webview?如何在webview中加载网页?
- web如何调用android?
对于第一点,不是本文的重点,你可以参考这个链接来自行解决。
对于第二点,我们继续分析。
web和android,就像是一个**人和一个美国人。想要不同语言之间的人进行有效沟通的话,必须要满足以下两个基本要素。
- 你在听我说话 => 你在监听我的动作 =>javascript的某个行为会被java捕获到
- 我们手上有一本中英双译字典 => 我们之间有协议规范 => 对于传输的信息,javascript和java要用规定好的方式来编码和解码
先来看动作监听。javascript的哪些操作会被java捕获到?整个web页面都是运行在java提供的webview实例当中。javascript执行以下四种行为会被webview监听到,箭头后面是对应触发的Java方法。
- window.alert => onJSAlert
- window.confirm => onJSConfirm
- window.prompt => onJsPrompt
- window.location => shouldOverrideUrlLoading
好,动作监听我们解决了。接下来是要制定通信协议。说到通信协议,我们首先想到的是http协议,没错,我们可以仿照http协议制定我们自己的协议。
比如http协议是这样子的: http://www.baidu.com?param
我们制定的协议是这样子: ywjs://toast?message
其中ywjs是scheme,代表我们用的是某种自定义的协议。toast是方法名,也就是我希望调用的是系统的toast方法。toast方法所需要的参数,也就是弹出的提示字符串。
代码实现
好,思路我们已经分析好了。万事俱备,只欠敲代码了。(如果完全看不懂,请先补充一下基本的安卓开发知识和webview的使用方法)
// java代码 JSBridgeAlert.java
package com.example.youngwind.helloworld.JSBridgeAlert;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import com.example.youngwind.helloworld.R;
public class JSBridgeAlert extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jsbridge_alert);
// 添加了chrome:inspect bebug功能
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE)) {
WebView.setWebContentsDebuggingEnabled(true);
}
}
final WebView myWebView = (WebView) findViewById(R.id.webView);
// webview启用javascript
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.loadUrl("file:///android_asset/alert.html");
myWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
// message === "ywjs://toast?利用jsAlert建立JSBridge"
if(message.startsWith("ywjs://")){
String methodName = "";
String param = "";
Uri uri = Uri.parse(message);
// toast
methodName = uri.getHost();
// msg参数
param = uri.getQuery();
if (methodName.equals("toast")) {
Toast.makeText(view.getContext(), param, Toast.LENGTH_LONG).show();
}
// 这一句非常关键,相当于点击了alert的确认
// 没有这一句的话,alert只能被触发一次,且其他地方的alert也不能被触发
result.confirm();
}
return true;
}
});
}
}
// html代码 Alert.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>利用jsAlert建立JSBridge</title>
<style>
#test {
width:100%;
height:50px;
}
</style>
</head>
<body>
<button id="test">点我toast</button>
<script src="file:///android_asset/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).on('click','#test',function(){
alert("ywjs://toast?利用jsAlert建立JSBridge");
})
</script>
</body>
</html>
其他实现方式
刚刚只展示了onJsAlert的实现代码,就像刚刚提到的,还有confirm、prompt和location方法也可以用来实现。
另外,webview本身提供了这样的接口,可以不采用上面的重新思路来进行,那就是addJavascriptInterface。
具体的实现方法可以参考文末的链接,可以查看我编写的demo代码。
遗留问题
还有这么一些问题没有有待解决。
- java如何调用js方法?
- Javascript调用了java方法,java方法执行完成之后如何调用javascript的回调方法?
- ios系统下如何做JSBridge?
参考资料
- http://blog.csdn.net/sbsujjbcy/article/details/50752595
- http://rensanning.iteye.com/blog/2043049
- https://github.com/pedant/safe-java-js-webview-bridge
- http://blog.csdn.net/leehong2005/article/details/11808557
- http://blog.csdn.net/jackyhuangch/article/details/8310033
- http://droidyue.com/blog/2014/07/09/override-javascript-alert-in-android/index.html
- Demo源码: https://github.com/youngwind/android-demo
addJavascriptInterface
和制定的协议
有什么区别?