按安卓开发目前现状来说,开发者大部分时间还是花在UI的屏幕适配上,使用原生控件开发成本已不是那么理想,鉴于很多项目和ios基于一致的ui界面,至使安卓UI开发成本花费更大的代价,因此目前结合Html5和原生控件是解决UI适配的一种很好的选择,处于APP性能也会用java和native层进行结合。不管是哪种结合,其实原理都差不多,只要按照它的协议来,是很容易的,今天我们仅对于Html和Java层结合,学习下WebViewJavascriptBridge。
一 什么是webViewjavascripBridge?
WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,用作者的话来说就是实现java(ios为oc)和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得我们的开发更加灵活和安全。
二 为什么要用webViewjavascripBridge?
- mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");
4.4之后调用需要在调用方法加入加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对安卓客户端的窃取和攻击。
但是即使这样,我们很多时候需要在js记载本地代码的时候,要做一些判断和限制,或者有可能也会做些过滤和对用户友好提示,因此JavascriptInterface也就无法满足我们的需求了,特此有大神就写出了WebViewJavascriptBridge框架。
三 怎么使用webViewjavascripBridge
1 将jsBridge.jar引入到我们的工程
repositories { // ... maven { url "https://jitpack.io" } } dependencies { compile 'com.github.lzyzsd:jsbridge:1.0.4' }
2 WebView包需使用以上包的webView
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <!-- button 演示Java调用web -->
- <Button
- android:id="@+id/button"
- android:layout_width="match_parent"
- android:text="@string/button_name"
- android:layout_height="48dp"
- />
- <!-- webview 演示web调用Java -->
- <com.github.lzyzsd.jsbridge.BridgeWebView
- android:id="@+id/webView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- </com.github.lzyzsd.jsbridge.BridgeWebView>
- </LinearLayout>
3 Java代码
- public class MainActivity extends Activity implements OnClickListener {
- private final String TAG = "MainActivity";
- BridgeWebView webView;
- Button button;
- int RESULT_CODE = 0;
- ValueCallback<Uri> mUploadMessage;
- static class Location {
- String address;
- }
- static class User {
- String name;
- Location location;
- String testStr;
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- webView = (BridgeWebView) findViewById(R.id.webView);
- button = (Button) findViewById(R.id.button);
- button.setOnClickListener(this);
- webView.setDefaultHandler(new DefaultHandler());
- webView.setWebChromeClient(new WebChromeClient() {
- @SuppressWarnings("unused")
- public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType, String capture) {
- this.openFileChooser(uploadMsg);
- }
- @SuppressWarnings("unused")
- public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType) {
- this.openFileChooser(uploadMsg);
- }
- public void openFileChooser(ValueCallback<Uri> uploadMsg) {
- mUploadMessage = uploadMsg;
- pickFile();
- }
- });
- //加载本地网页
- //webView.loadUrl("file:///android_asset/demo.html");
- //加载服务器网页
- webView.loadUrl("https://www.baidu.com");
- //必须和js同名函数,注册具体执行函数,类似java实现类。
- webView.registerHandler("submitFromWeb", new BridgeHandler() {
- @Override
- public void handler(String data, CallBackFunction function) {
- String str ="这是html返回给java的数据:" + data;
- // 例如你可以对原始数据进行处理
- makeText(MainActivity.this, str, LENGTH_SHORT).show();
- Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
- function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));
- }
- });
- //模拟用户获取本地位置
- User user = new User();
- Location location = new Location();
- location.address = "上海";
- user.location = location;
- user.name = "Bruce";
- webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
- @Override
- public void onCallBack(String data) {
- makeText(MainActivity.this, "网页在获取你的位置", LENGTH_SHORT).show();
- }
- });
- webView.send("hello");
- }
- public void pickFile() {
- Intent chooserIntent = new Intent(Intent.ACTION_GET_CONTENT);
- chooserIntent.setType("image/*");
- startActivityForResult(chooserIntent, RESULT_CODE);
- }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
- if (requestCode == RESULT_CODE) {
- if (null == mUploadMessage){
- return;
- }
- Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
- mUploadMessage.onReceiveValue(result);
- mUploadMessage = null;
- }
- }
- @Override
- public void onClick(View v) {
- if (button.equals(v)) {
- webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {
- @Override
- public void onCallBack(String data) {
- // TODO Auto-generated method stub
- Log.i(TAG, "reponse data from js " + data);
- }
- });
- }
- }
- }
- class MyWebViewClient extends BridgeWebViewClient{
- public MyWebViewClient(BridgeWebView webView) {
- super(webView);
- }
- }
- mWebView.setWebViewClient(new MyWebViewClient(mWebView));
上面句话很关键:
- //必须和js函数同名,注册具体执行回调函数,类似java实现类。
- webView.registerHandler("submitFromWeb", new BridgeHandler() {
- @Override
- public void handler(String data, CallBackFunction function) {
- String str ="这是html返回给java的数据:" + data;
- // 例如你可以对原始数据进行处理
- makeText(MainActivity.this, str, LENGTH_SHORT).show();
- Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
- function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));
- }
- });
如果是webview刚开始就执行一段Java代码 ,可以通过webView.CallHandler()来实现 。当然我们注册的方法也要和js里面的方法名一致。
- webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
- @Override
- public void onCallBack(String data) {
- makeText(MainActivity.this, "网页在获取你的位置", LENGTH_SHORT).show();
- }
- });
3 Html和js实现
html代码如下,效果图
- <html>
- <head>
- <meta content="text/html; charset=utf-8" http-equiv="content-type">
- <title>
- js调用java
- </title>
- </head>
- <body>
- <p>
- <xmp id="show">
- </xmp>
- </p>
- <p>
- <xmp id="init">
- </xmp>
- </p>
- <p>
- <input type="text" id="text1" value="用户名(username)" />
- </p>
- <p>
- <input type="text" id="text2" value="password" />
- </p>
- <p>
- <input type="button" id="enter" value="发消息给Native" onclick="testClick();"
- />
- </p>
- <p>
- <input type="button" id="enter1" value="调用Native方法" onclick="testClick1();"
- />
- </p>
- <p>
- <input type="button" id="enter2" value="显示html" onclick="testDiv();" />
- </p>
- <p>
- <input type="file" value="打开文件" />
- </p>
- </body>
- </html>
js代码
- <script>
- function testDiv() {
- document.getElementById("show").innerHTML = document.getElementsByTagName("html")[0].innerHTML;
- }
- function testClick() {
- var str1 = document.getElementById("text1").value;
- var str2 = document.getElementById("text2").value;
- //发送消息给java代码
- var data = "name=" + str1 + ",pass=" + str2;
- window.WebViewJavascriptBridge.send(
- data
- , function(responseData) {
- document.getElementById("show").innerHTML = "repsonseData from java, data = " +responseData
- }
- );
- }
- function testClick1() {
- var str1 = document.getElementById("text1").value;
- var str2 = document.getElementById("text2").value;
- //调用本地java方法
- window.WebViewJavascriptBridge.callHandler(
- 'submitFromWeb'
- , {'param': str1}
- , function(responseData) {
- document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
- }
- );
- }
- function bridgeLog(logContent) {
- document.getElementById("show").innerHTML = logContent;
- }//注册事件监听
- function connectWebViewJavascriptBridge(callback) {
- if (window.WebViewJavascriptBridge) {
- callback(WebViewJavascriptBridge)
- } else {
- document.addEventListener(
- 'WebViewJavascriptBridgeReady'
- , function() {
- callback(WebViewJavascriptBridge)
- },
- false
- );
- }
- }
- //注册回调函数,第一次连接时调用 初始化函数
- connectWebViewJavascriptBridge(function(bridge) {
- bridge.init(function(message, responseCallback) {
- console.log('JS got a message', message);
- var data = {
- 'Javascript Responds': 'Wee!'
- };
- console.log('JS responding with', data);
- responseCallback(data);
- });
- bridge.registerHandler("functionInJs", function(data, responseCallback) {
- document.getElementById("show").innerHTML = ("data from Java: = " + data);
- var responseData = "Javascript Says Right back aka!";
- responseCallback(responseData);
- });
- })
- </script>
这段代码不难理解,我们对上面的id为enter的Button注册了一个点击事件,点击后执行以下testClick()方法,依次类推,其他Button注册事件相同,
当点击“发消息给Native”的按钮时,Js通过webWiew的jsBridage.send()发送一条数据给java层(密码和用户名),同时在此用function()来执行应java层回调函数的。此demo中是把java返回的数据插入到"show"的div里面去。
testClick1():此方法中调用callHandler来调用Java代码的submitFromweb同名函数,可以结合上面的Activty的代码理解下,此函数调用我们已在java注册实现好的
- //必须和js同名函数,注册具体执行函数,类似java实现类。
- webView.registerHandler("submitFromWeb", new BridgeHandler() {
- @Override
- public void handler(String data, CallBackFunction function) {
- String str ="这是html返回给java的数据:" + data;
- // 例如你可以对原始数据进行处理
- makeText(MainActivity.this, str, LENGTH_SHORT).show();
- Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
- function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));
- }
- });
java注册Js函数如上面列子 ,那么在js中注册方法来注册呢,
在js中我们同样可以直接注册一个回调函数,通过 bridge.registerHandler()来注册,接着调用 responseCallback(responseData)来返回数据给java代码的同名函数。f
也可以直接调用init()来指定网页首次加载上面注册java代码。
- connectWebViewJavascriptBridge(function(bridge) {
- bridge.init(function(message, responseCallback) {
- console.log('JS got a message', message);
- var data = {
- 'Javascript Responds': 'Wee!'
- };
- console.log('JS responding with', data);
- responseCallback(data);
- });
- bridge.registerHandler("functionInJs", function(data, responseCallback) {
- document.getElementById("show").innerHTML = ("data from Java: = " + data);
- var responseData = "Javascript Says Right back aka!";
- responseCallback(responseData);
- });
- })