在android hybrid app开发过程中,经常要面对的一个问题是java与js的通信。android程序是由dalvick虚拟机来运行,javascript是由webview的webkit引擎来解析执行,本质上应该是dalvick虚拟机的一部分,所以两者通信是要看android底层api留了多少口子出来。
一、js向java传递数据(js调用java)
1.android提供给开发者的是往js中注入java对象的方式,即java层调用addJavascriptInterface(Object javaObject,String name),js中使用window.name.javaObject中的方法进行调用。
官方api中给出的例子是
class JsObject {
@JavascriptInterface
public String toString() {
return "injectedObject";
}
}
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
这种方式需要注意有以下几点(取自官方文档):
用汉语说,
1.JsObject.toString()是在js运行的线程(非UI线程)中同步执行,方法是可以带有返回值的(可以顺带带点java层数据回js层)。
2.这种方法使用起来很方便,但在android api16及以下的系统上是有安全漏洞的,漏洞的原因和实例具体可见
Android WebView远程执行代码漏洞浅析,
WebView 远程代码执行漏洞浅析,
Android WebView的Js对象注入漏洞解决方案。
3.对于漏洞,官方肯定是会在新的版本中做修复的。
官方对于API16以上的版本的处理是增加@JavascriptInterface注解,通过该注解标示被注入js中的java对象可以调用的方法,非被标示的方法是不可以被调用的,这样通过反射来执行恶意代码的路就走不通了。但是官方对于API16及以下的版本就不处理了。
4.那为了兼容API16及以下的版本,应该如何做呢?
通常有两种方法,
一种是js中调用iframe.src或iframe.href或window.open(‘www.example.com?body=loadurl’)等方式将数据放在url中,java层通过WebviewClient的shouldOverrideUrlLoading(Webview webview, String url)拦截url,实现js往java传递数据,github上的jsBridge项目使用的是iframe的src方式。
另一种是js中调用prompt()方法,java层通过WebChromeClient的onJsPrompt(Webview webview, String url, String message, String defaultValue, JsPromptResult result)的方式来获取数据。这两种方法都是异步方式,都是在UI线程中执行。
二、java向js传递数据(java调用js的方式)
当API小于19时使用Webview.loadUrl(“javascript:”+js),大于19时通过evaluateJavascript(String js,ValueCallback resultCallback)的方式。
三、Java和js交互有以下一些特点(取自百度经验):
1.Java 调用 js 里面的函数,速度并不令人满意,大概一次一两百毫秒吧,如果要做交互性很强的事情,这种速度会让人疯掉的。而反过来就不一样了, js 去调 java 的方法,速度很快,基本上 40-50 毫秒一次。所以尽量用 js 调用 java 方法,而不是 java 去调用 js 函数。
2.Java 调用 js 的函数,没有返回值,而 Js 调用 java 方法,可以有返回值。返回值可以是基本类型、字符串,也可以是对象。如果是字符串,有个很讨厌的问题,第 3 点我会讲的。如果是对象,这个对象会被转换为 js 的对象,直接可以访问里面的方法。但是我不推荐 java 返回给 js 的是对象,除非是必须。因为 js 收到 java 返回的对象,会产生一些交换对象,而如果这些对象的数量增加到了 500 或 600 以上,程序就会出问题。所以尽量返回基本数据类型或者字符串。
3.Js 调用 Java 的方法,返回值如果是字符串,你会发现这个字符串是 native 的,不能对它进行一些修改操作,比如想对它 substr ,取不到。怎么解决呢?转成 locale 的。使用 toLocaleString() 函数就可以了。不过这个函数的速度并不快,转化的字符串如果很多,将会很耗费时间。