Android——JsBridge实战(一)

Android开发目前现状来说,开发者大部分时间花在UI的屏幕适配上,使用原生控件开发成本已不是那么的理想,鉴于很多项目保持和iOS一致的UI界面风格,至使移动UI开发成本花费更大的代价,因此目前结合H5和原生控件混合开发是解决UI适配的一种很好的选择, 因此基于网页形式的插件更新业务功能出现了,处于APP性能的考虑,Android也会使用java和native层(C,C++)进行结合。无论是哪种结合,其实原理都差不多,只要按照它的协议来是很容易的,今天我们仅对于H5和Java层结合的混合开发,了解WebViewJavascriptBridge的使用。

最新推荐使用DSBridge
特性
1、Android、IOS、Javascript 三端易用,轻量且强大、安全且健壮。
2、同时支持同步调用和异步调用
3、支持以类的方式集中统一管理API
4、支持API命名空间
5、支持调试模式
6、支持API存在性检测
7、支持进度回调:一次调用,多次返回
8、支持Javascript关闭页面事件回调
9、支持Javascript 模态/非模态对话框
10、支持腾讯X5内核

什么是JsBridge

WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,用作者的话来说就是实现java(ios为oc)和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得开发者更方便的让js和native灵活交互,使我们的开发更加灵活和安全。

JSBridge的优点

Android API 4.4以前,谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户端的重要信息,甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,在API 4.4以后增加了防御措施,如果用js调用本地代码,开发者必须在代码申明JavascriptInterface, 列如在4.0之前我们要使得webView加载js只需如下代码:

mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");

4.4之后使用时需要在调用Java方法加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对客户端的进行窃取和攻击。 但是即使这样,我们很多时候需要在js调用本地java代码的时候,要做一些判断和限制,或者有的场景也会做些过滤或者对用户友好提示,甚至更复杂的Hybrid模式下,需要js和native之间进行交互通讯,拍照上传,因此原生的JavascriptInterface 就比较维护了,特此有了基于JavascriptInterface 封装的WebViewJavascriptBridge框架。

#使用JsBridge正确姿势

1 添加依赖

  • Eclipse:
    导入jar包
    直接copy jar的源码到工程
    JsBridge. jar可以到gitHub上直接下载。

  • Android Studio:
    配置gradle

 repositories {
	 //...
     maven { url "https://jitpack.io" }
 }

 dependencies {
	 compile 'com.github.lzyzsd:jsbridge:1.0.4'
 }

2 xml中使用jsBridge的webView

 <com.github.lzyzsd.jsbridge.BridgeWebView
        android:id="@+id/activity_web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

##3 JS和Native交互:
JSBridge是基于订阅和回调来实现Js和native交互的,我们需要在java中订阅,然后Js中回调,反之也可以。
###3.1 Java中使用JsBridge

  • registerHandler()用来注册一个java函数,来实现js回调的handler,

参数说明:
第一个 : 订阅的方法名
第二个 : 回调Handler , 参数返回js请求的resqustData,function.onCallBack()回调到js,调用function(responseData)
//必须和js同名函数,注册具体执行函数,类似java实现类

webView.registerHandler("submitFromWeb", new BridgeHandler() {
	@Override
	public void handler(String data, CallBackFunction function) {
		function.onCallBack( data + “java”);
	}
});
  • CallHandler( )用来调用js中的注册的方法

参数说明:
第一个 : js中注册的方法名
第二个 : java发给js的参数,json格式的string
第三个 : 用来处理js回复给java的信息的回调方法

 mWebView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
                showToast(data);
            }
        });
  • send()方法用来向js发送消息
mWebView.send("Hello!");

3.2 JS中使用JsBridge

  • window.WebViewJavascriptBridge.callHandler:调用java层注册的同名函数,方法名必须和Java层保持一致

参数说明:
第一个 : 方法名
第二个 : js调用native的请求参数
第三个:js在被回调后具体执行方法,responseData为java层回传jsonStr.

window.WebViewJavascriptBridge.callHandler('submitFromWeb',
{'param': requsetData }, function(responseData) {
	// do something
});
  • bridge.registerHandler:注册一个js方法,java层可调用此方法

参数说明:
第一个:方法名
第二个:回调方法,用于接收java层回复给js的消息;responseCallback()用于回调回java层

bridge.registerHandler("functionInJs",function(data,responseCallback){
	alert("data from Java = " + data);
							 document.getElementById("msgFromJava").innerHTML = "data from Java = " + data;
	var responseData = "Javascript Says Right back aka!";  
	responseCallback(responseData);
});
  • window.WebViewJavascriptBridge.send:js向java层发送消息

参数说明:
第一个:js发给java的消息
第二个:回调方法,接收java回复给js的消息

window.WebViewJavascriptBridge.send(data,function(responseData){
	alert(responseData);
});

代码演示

1.Activity

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.mWebView)
    BridgeWebView mWebView;

    private String webUrl = "file:///android_asset/demo.html";
    //private String webUrl = "https://www.baidu.com";
    private static CallBackFunction mfunction;
    int RESULT_CODE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ButterKnife.bind(this);

        initWebView();
    }

    private void initWebView() {
        mWebView.setWebViewClient(new MyWebViewClient(mWebView));
        mWebView.setDefaultHandler(new MyHandlerCallback());
        mWebView.setWebChromeClient(new WebChromeClient());
        //加载服务器网页或者本地网页
        mWebView.loadUrl(webUrl);

        //必须和js同名函数,注册具体执行函数,类似java实现类。
        mWebView.registerHandler("submitFromWeb", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                //对原始数据进行处理
                String str = "这是html返回给java的数据:" + data;
                str += ",对原始数据进行了截取操作:" + str.substring(0, 5);
                showToast(str);
                //回调返回给JS
                function.onCallBack(str);
            }
        });

		//打开本地文件
        mWebView.registerHandler("openFile", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                pickFile();
                mfunction = function;
            }
        });

        //模拟用户获取本地位置
        User user = new User();
        user.name = "alice";
        Location location = new Location();
        location.address = "上海";
        user.location = location;
        //webView调用js的方法
        mWebView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
                showToast(data);
            }
        });
        mWebView.send("Hello!");
    }

    private void showToast(String string) {
        Toast.makeText(MainActivity.this, string, Toast.LENGTH_SHORT).show();
    }

	 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) {
            Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
            mfunction.onCallBack(result.toString());
        }
    }

    class MyWebViewClient extends BridgeWebViewClient {

        public MyWebViewClient(BridgeWebView webView) {
            super(webView);
        }
    }

    class MyHandlerCallback extends DefaultHandler {
        @Override
        public void handler(String data, CallBackFunction function) {
            super.handler(data, function);
            if (null != function) {
                showToast(data);
                function.onCallBack("收到JS消息回复。");
            }
        }
    }

    static class Location{
        String address;
    }

    static class User{
        String name;
        Location location;
        String testStr;
    }

}

2.JS

<html>
<head>
    <meta content="text/html; charset=utf-8" http-equiv="content-type">
    <title>js调用java</title>
    <script>
            //注册事件监听
            function connectWebViewJavascriptBridge(callback){
                if(window.WebViewJavascriptBridge){
                    callback(WebViewJavascriptBridge);
                }else{
                    document.addEventListener(
                        "WebViewJavascriptBridgeReady",
                        function(){
                            callback(WebViewJavascriptBridge);
                        },
                        false
                    );
                }
            }

             //注册回调函数,第一次连接时调用 初始化函数
			connectWebViewJavascriptBridge(function(bridge){
				bridge.init(function(message, responseCallback){
					alert("JS get a message : " + message);
					var data = {
						"JavaScript Responds" : "Wee!" 
					};
					//alert("Js response data : " + data);
					responseCallback("Js response data wee ");
				});
				
					        bridge.registerHandler("functionInJs",function(data,responseCallback){
				    alert("data from Java = " + data);
					document.getElementById("msgFromJava").innerHTML = "data from Java = " + data;
					var responseData = "Javascript Says Right back aka!";  
					responseCallback(responseData);
				});
				
			})

			function sendMsgToNative(){
				var name = document.getElementById("username").value;
				var pwd = document.getElementById("password").value;

                //调用本地java方法
				window.WebViewJavascriptBridge.callHandler("submitFromWeb",
				{"name":name},
				function(responseData){
				    alert("data from Java = " + responseData);
					document.getElementById("msgFromJava").innerHTML = "data from Java = " + responseData;
				});

				var data = "name=" + name + ",pwd=" + pwd;
				//发送消息给java代码
				window.WebViewJavascriptBridge.send(data,function(responseData){
				     alert(responseData);
				});
			}

			function openFile(){	           window.WebViewJavascriptBridge.callHandler("openFile","",function(responseData){
			        //alert("openFile response : " + responseData);
			        document.getElementById("pic").src = responseData;
			    });
			}
    </script>
</head>

<body>
<p>用户名:<input type="text" id="username"/></p>
<p>密码:<input type="text" id="password"/></p>
<p>Java信息测试:<input type="text" id="msgFromJava"/></p>
<p><input type="button" value="发消息给Native" onclick="sendMsgToNative();"/></p>
<p><input type="button" value="调用Native方法"/></p>
<p><input type="button" value="显示html"/></p>
<p><input type="file" value="选择文件" onclick="openFile();"/></p>
<img id="pic" src="" width="100%" height="auto"/>
</body>
</html>

效果图:
这里写图片描述
注意事项:
无论怎样形式的交互,Js 必须要初始化jsBridge

四 总结

通过以上的API介绍,代码示例,不难发现此框架的优雅和简便,对js和java双方来说,如果Html中的js需要调用java代码,而java代码没做任何实现,那么js中方法也是无效的,反之java代码注册的函数,没在js里去回调实现,那么Java层也是无法获取js中数据的,由此可见,此通信是双方支持的,必须由双方来约定,这样就避免了Android之前存在的js注入漏洞,也很大的提高了安全性,也可以保证我们的网页数据不被第三方的APP获取,具体来讲,列如我们的项目某一个web的h5界面,被系统浏览器或者其他第三方App的恶意加载,那么它的java代码想调用你的js函数,实现需要你的H5的Js先注册,不然跟本无法调用你的h5信息。这样保证了这个html数据的安全性,,第三方的浏览器可以加载预览你的网页,但是第三方java无法和你的的h5中的js交互通信的。同样加载我们自己的APP加载第三方的网页时候,我们可以对第三方网页进行一些行为的过滤,方便保护我们手机的安全,列如第三方可以获取本机地址时我们可以提示用户授权。虽然H5并不属于插件的一种,但是借助h5我可以方便的去更新一些运营活动,和某些需要经常需要更换UI的业务功能的地方,以上只JSBridge的使用姿势,,以后再给大家解剖下JsBridge的内部实现。

参考内容:
项目地址:
项目实例:https://github.com/NeglectedByBoss/JsBridge_Android
英文介绍:https://github.com/lzyzsd/JsBridge

JsBridage的封装请参考:Android基于JsBridge封装的高效带加载进度的WebView

Android 利用WebViewJavascriptBridge 实现js和java的交互(一)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值