JSbridge系列解析(一):JS-Native调用方法

JSBrige系列直通车,由浅入深理解JS-Native的通信过程:
JSbridge系列解析(一):JS-Native调用方法
JSbridge系列解析(二):lzyzsd/JsBridge使用方法
JSbridge系列解析(三):lzyzsd/JsBridge源码解析
JSbridge系列解析(四):Web端发消息给Native代码流程具体分析

web具有开发速度快,上线方便等优点;而native开发则展示效果好,但开发节奏慢。在开发中,经常需要将web开发和native开发相结合,这就需要了解JS和Native之间相互调用的方法了。

基本知识

安卓提供Webview用来加载html页面,以安卓4.4系统为分水岭,之前的webview内核采用webkit;4.4及之后改为chromium内核。

JS -> Native

常用的JS调用Java代码的方法,主要包括以下三种:
1)通过WebView的addJavascriptInterface进行对象映射
2)通过 WebViewClient 的shouldOverrideUrlLoading方法回调拦截 url
3) 通过 WebChromeClient 的onJsAlert、onJsConfirm、onJsPrompt方法回调拦截JS对话框alert、confirm、prompt 消息

今天主要介绍第一种,也是最常用的使用方案。首先需要打开WebView的一个设置选项。如下:

//允许运行js代码
mWebView.getSettings().setJavaScriptEnabled(true); 

WebView提供addJavascriptInterface方法,用于注入Java函数以便JS调用。示例如下:

/**
This method can be used to allow JavaScript to control the host application. This is a powerful feature, but also presents a security risk for applications targeted to API level [JELLY_BEAN](http://developer.android.com/reference/android/os/Build.VERSION_CODES.html#JELLY_BEAN)
 or below, because JavaScript could use reflection to access an injected object's public fields. Use of this method in a WebView containing untrusted content could allow an attacker to manipulate the host application in unintended ways, executing Java code with the permissions of the host application. Use extreme care when using this method in a WebView which could contain untrusted content.
JavaScript interacts with Java object on a private, background thread of this WebView. Care is therefore required to maintain thread safety.
The Java object's fields are not accessible
*/
//注入js对象jsInterface
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");  

根据安卓官方的api说明,addJavaScriptInterface方法存在安全漏洞。因为JS可以通过反射访问注入对象。官方在4.2及以后的系统中修复了该问题,要求注入的远程方法必须使用注解@JavascriptInterface

//JAVA代码
class JsObject {  
   @JavascriptInterface  
   public String toString() { return "injectedObject"; }  
}  
webView.addJavascriptInterface(new JsObject(), "injectedObject");  

//JS调用注入对象示例【java代码】
webView.loadUrl("javascript:alert(injectedObject.toString())");  

针对4.2以下的系统,可以利用JS的事件来解决,如prompt, alert等。这样的动作都会对应到WebChromeClient类中相应的方法,对于prompt,它对应的方法是onJsPrompt方法,这个方法的声明如下:

public boolean onJsPrompt(WebView view, String url, String message,   
    String defaultValue, JsPromptResult result)  

prompt方案可参考PhoneGap的实现,主要包括以下几点
【1】在4.2以下系统,js对象未注入的情况下,可让JS调用prompt方法,通过prompt把JS中的信息传递到Java部分。这些信息应该是我们定义的一段有特定含义的文本,可能包含:特定标识,方法名称,参数等。在onJsPrompt方法中,我们去解析传递过来的文本,得到方法名,参数等,再通过反射机制,调用指定的方法,从而调用到Java对象的方法。
【2】关于返回值,可以通过prompt返回回去,这样就可以把Java中方法的处理结果返回到Js中。
【3】我们需要动态注入一段JavaScript初始化的脚本,可通过webview的loadUrl来加载,从而注册到html页面中。如PhoneGap中的js文件

注意:1)注入JS方法不能过早,一般在onPageFinished方法,否则注入可能无效
2)在Android 3.0以下,安卓系统添加了一个名为searchBoxJavaBridge_的Js接口,需要调用removeJavascriptInterface方法删除该方法,以解决安全风险。
3)记忆中在4.4系统之前,onJsPrompt运行在主线程中;而4.4及以后,该方法运行在非主线程。请各位看客验证

Native -> JS

Java调用JS的方法,包括以下两种:
1)通过WebView的loadUrl方法,该方法必须在主线程中调用
2)通过WebView的evaluateJavascript,该方法可获取JS返回值,但4.4及以上才能使用

推荐阅读更多精彩内容