Android中webview与native之间的交互方式(jsbridge)
前言
随着H5的广泛使用,Android开发过程中免不了会使用网页来做展示,那么,web与native之间的通信就显得尤其重要了,其实际上是JavaScript与java之间的通信;如图所示,我们开发过程中需要在native中调用JavaScript,或者是在JavaScript中调用native。
* JavaScript调用java
JavaScript调用java的方式可以分为两类,一是通过捕获url scheme的方式,二是利用原生接口实现调用。
1. 捕获url scheme的方式
核心思想是:web端与native首先协商好通信中使用的url的格式,紧接着web端通过一定的方式将url发送出去,最后native层捕获url,并进行分析后再去调用原生方法。这种方法也是目前被广泛使用的方式。
a) 首先第一步是约定好url的格式,例如这里约定为:
JSBridge://bridge:129129723/showToast?{"msg":"Hello JSBridge"}
其中showToast为需要调用的native层方法,{“msg”:”Hello JSBridge”}为向native传递的json数据。
b) 约定好传递格式后,web端需要触发native层去捕获url。
Android中为我们提供了两个类WebViewClient和WebChromeClient,这两个类分别为我们提供了一些方法可以使用。当有任何url在webview中使用时都会被WebViewClient的shouldOverrideUrlLoading函数拦截,因此我们可以利用这个特性,在JavaScript中构建一个1像素的iframe来触发这个函数。web端代码如下:
var url = 'JSBridge://bridge:129129723/showToast?{"msg":"Hello JSBridge"}';
var iframe = document.createElement('iframe');
iframe.style.width = '1px';
iframe.style.height = '1px';
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
Android端代码:
webView = (WebView) findViewById(R.id.webView);
webView.setVerticalScrollbarOverlay(true);
//设置WebView支持JavaScript
webView.getSettings().setJavaScriptEnabled(true);
String url = "file:///android_asset/test.html"; //加载本地html
webView.loadUrl(url);
webView.setWebViewClient(new JsbridgeWebViewClient());
其中JsbridgeWebViewClient继承WebViewClient,并重写shouldOverrideUrlLoading方法,并在里面实现调用原生方法的逻辑。
其次,WebChromeClient提供了三个原生的方法,当在JavaScript中使用window.alert,window.confirm,window.prompt三个方法时会相应的触发WebChromeClient对象的onJsPrompt、onJsAlert、onJsConfirm方法,所以我们也可以在前端通过这三种方式触发native捕获url。web端代码如下:
var url = 'JSBridge://bridge:129129723/showToast?{"msg":"Hello JSBridge"}';
window.prompt(uri, "");
Android端代码只要将上面的JsbridgeWebViewClient继承WebChromeClient ,并重写onJsPrompt、onJsAlert、onJsConfirm方法即可,这里就不再重复。
c) native层捕获到url后,如何调用?
通过解析url,可以得到需要调用的native方法名以及json数据;这里举例是调用native层的showToast方法,我采用反射的方式来进行调用,核心代码:
“`
public static void showToast(WebView webView, JSONObject param) {
String message = param.optString(“msg”);
Toast.makeText(webView.getContext(), message, Toast.LENGTH_SHORT).show();
}
public static String callJava(WebView webView, String uriString, Object owner) {
String methodName = “”;
String className = “”;
String param = “{}”;
String port = “”;
if (!TextUtils.isEmpty(uriString) && uriString.startsWith(“jsbridge”)) {
Uri uri = Uri.parse(uriString);//解析url
className = uri.getHost();
param = uri.getQuery();
port = uri.getPort() + “”;
String path = uri.getPath();
if (!TextUtils.isEmpty(path)) {
methodName = path.replace(“/”, “”);
}
}
Class classType = owner.getClass();
Method showToastMethod = null;
try {
showToastMethod = classType.getMethod(methodName,new Class[]{WebView.class,JSONObject.class});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
showToastMethod.invoke(owner, webView, new JSONObject(param));//反射调用showToast方法
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
“`
以上是通过捕获url scheme实现JavaScript调用java的方式。
**2. 使用原生方法 **addJavascriptInterface
Android为我们提供了addJavascriptInterface方法,JavaScript可以直接调用java方法,但该方法在Android 4.2以下是存在安全隐患的,可参考,官方在Android4.2进行了修复,在使用addJavascriptInterface方法时,必须在提供给JavaScript调用的方法前添加@JavascriptInterface。例如在Android端将showInfoFromJs和getInfoFromJs提供给js调用:
private class JsInterface {
private Context mContext;
public JsInterface(Context context) {
this.mContext = context;
}
//在js中调用window.test.showInfoFromJs(name),便会触发此方法。
@JavascriptInterface
public void showInfoFromJs(String name) {
Toast.makeText(mContext, name, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void getInfoFromJs(int a, int b){
int c = a+b;
Toast.makeText(mContext, ""+c, Toast.LENGTH_SHORT).show();
}
}
//设置WebView支持JavaScript
webView.getSettings().setJavaScriptEnabled(true);
String url = "file:///android_asset/test.html";
webView.loadUrl(url);
//在js中调用本地java方法
webView.addJavascriptInterface(new JsInterface(this), "test");
在js中通过以下进行调用:
var name = document.getElementById("name_input").value;
var a = ;
var b = ;
window.test.showInfoFromJs(name);
window.test.getInfoFromJs(a,b);
-
java调用JavaScript
java调用javascript的方法比较简单,因为android为我们提供了相应的方法, 4.4之前通过loadUrl的方式,而在4.4之后提供了evaluateJavascrip异步调用的方式。这里就只举例loadurl的方法。
以上就是js与java互相调用的方式。String msg = ((EditText) findViewById(R.id.input_et)).getText().toString(); //调用js中的函数:showInfoFromJava(msg) webView.loadUrl("javascript:showInfoFromJava('" + msg + "')");