Android WebView、js交互方式原理总结

前言:有些事,明知是错的,也要去坚持,因为不甘心;有些人,明知是爱的,也要去放弃,因为没有结局;有时候,明知没路了,却还在前进,因为习惯了。

精通android的人有,精通javascript的人亦有,但同时深入掌握两门语言的人少有!

android与js交互有两种方式,第一种是通过系统提供的@JavascriptInterface注解实现,第二种就是js注入。下面来详细讲解一下二者的使用方式,原理,区别。

一、@JavascriptInterface实现

实现步骤:
a.设置WebView支持js脚本
b.为提供给js调用的方法加上@JavascriptInterface注解
c.给WebView添加js接口

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JSMethod(mContext), "lh");
public class JSMethod {
    private Context mContext;

    public JSMethod(Context mContext) {
        this.mContext = mContext;
    }
    @JavascriptInterface
    public void toast(String msg) {
        Toast.makeText(mContext, msg == null ? "" : msg, Toast.LENGTH_SHORT).show();
    }
}
js端调用android方法:
lh.toast("Hello,China!");
android端执行js方法:
webView.loadUrl("javascript:console(" + "'Hello,China!'" + ")"");

二、js注入实现

先来说说原理吧,当js调用prompt()方法时,WebChromeClient.onJsPrompt()方法会被触发,当js触发Android提供的接口方法时,将该方法的方法名称、参数类型、参数值转成json,然后通过prompt方法传递给android端,android端解析json并通过反射执行对应的方法,同时也支持执行匿名回调。
整个流程比较复杂,看图:


为WebView绑定WebChormeClient监听,在Html加载进度25%时进行js注入(注入的js是根据android提供给js的对象类名动态生成);
动态注入的js代码如下:
javascript: (function(b) {
	console.log("HostApp initialization begin");
	var a = {
		queue: [],
		callback: function() {
			
			var d = Array.prototype.slice.call(arguments, 0);//获取该函数参数并转换为Array数组
			var c = d.shift();//取得数组第一个元素
			var e = d.shift();
			this.queue[c].apply(this, d);//新建一个对象 属性名称为取得的c,并将d数组作为他的值。然后将这个对象push到queue数组
			if(!e) {//e为空的时候,将queue数组属性名称为c的对象删除
				delete this.queue[c]
			}
		}
	};
	//各种赋值,最后都等于同一个函数
	a.alert = a.alert = a.alert = a.delayJsCallBack = a.getIMSI = a.getOsSdk = a.goBack = a.overloadMethod = a.overloadMethod = a.passJson2Java = a.passLongType = a.retBackPassJson = a.retJavaObject = a.testLossTime = a.toast = a.toast = function() {
		var f = Array.prototype.slice.call(arguments, 0);
		if(f.length < 1) {
			throw "HostApp call error, message:miss method name"
		}
		var e = [];
		//此段判断,然后赋值
		for(var h = 1; h < f.length; h++) {
			var c = f[h];
			var j = typeof c;
			e[e.length] = j;
			if(j == "function") {
				var d = a.queue.length;
				a.queue[d] = c;
				f[h] = d
			}
		}
		//将匿名对象{method: f.shift(),types: e,args: f}转换成json字符串并用浏览器弹出确认可输入框,然后取得输入框的值json序列化为js对象
		var g = JSON.parse(prompt(JSON.stringify({
			method: f.shift(),
			types: e,
			args: f
		})));
		if(g.code != 200) {
			throw "HostApp call error, code:" + g.code + ", message:" + g.result
		}
		return g.result
	};
	//获取a的属性值,然后循环
	Object.getOwnPropertyNames(a).forEach(function(d) {
		var c = a[d];
		//判断赋值
		if(typeof c === "function" && d !== "callback") {
			a[d] = function() {
				//concat 连接两个数组
				return c.apply(a, [d].concat(Array.prototype.slice.call(arguments, 0)))
			}
		}
	});
	b.HostApp = a;
	console.log("HostApp initialization end")
})(window);//闭包函数默认执行,然后赋给window。这样window.b就可以执行了 b.HostApp就是执行a的内容,但是a具体处理逻辑不对外开放,避免外部污染a内部逻辑
代码不难,可以自行理解,其中回调函数被封装在了a对象里面,确保android端可以通过webview.loadUrl()执行回调。
android端回调js代码如下:
javascript:HostApp.callback(0, 0 ,"call back haha");
android提供的每一个js方法都对应一个JsCallback对象,android就可以通过JsCallback对象来生成并执行回调js的代码。

三、优缺点

a.第一种方式不安全,不添加addJavascriptInterface,甚至默认false,在低于API17的WebView上默认添加"SearchBoxJavaBridge_"到mJavaScriptObjects中。这样就有可能通过用户信任的客户端获取SD卡的数据;
b.第一种方式必须要API大于等于17才能使用
c.第一种方式当有js回调函数需要android端执行时,都需要将匿名回调函数赋值给全局函数才能供android端回调,增加了js和android端通信的封装层的低效代码量;而第二种方式则是通过动态注入js的方式则非常方便。
d.第二种方式也有一定限制,比如android提供的方法必须是static修饰的,且方法第一个参数必须为WebView,不过这不影响使用。


最后,附上代码地址:点我













评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值