為什么使用JSBridge
<p>
在平時業(yè)務的開發(fā)中根悼,為了追求開發(fā)的效率以及移植的便利性赊时,一些展示性強的頁面我們會偏向于使用h5來完成,功能性強的頁面我們會偏向于使用native來完成垢乙,而一旦使用了h5,為了在h5中盡可能的得到native的體驗嘶炭,我們native層需要暴露一些方法給js調(diào)用,比如逊桦,彈Toast提醒眨猎,彈Dialog,分享等等强经,同樣我們也可以通過native方法去直接調(diào)用js的方法睡陪,所以 JsBridge 就是用來在 Android app的原生 java 代碼與 javascript 代碼中架設通信(調(diào)用)橋梁的輔助工具。
這里我們分析一下JSBridge的使用場景
- 1.JS調(diào)用JAVA的方法
- 2.JAVA調(diào)用JS的方法
- 3.JS調(diào)用JAVA方法后匿情,JAVA回調(diào)給JS
- 4.JAVA調(diào)用JS方法后兰迫,JS回調(diào)給JAVA
所以針對上面的使用場景,我們分別來實現(xiàn)
1.JS調(diào)用JAVA的方法
<p>
WebView 提供了一個接口码秉,可以讓我們注入 Java 對象到頁面中逮矛,這樣鸡号,頁面中的 javascript 就能直接訪問 Java 對象的接口转砖,從而實現(xiàn) Java 和 Javascript 的交互。
首先必須啟用 WebView 中的 Javascript 支持
mWebView.getSettings().setJavaScriptEnabled(true);
注入 Java 對象到 WebView 中
mWebView.addJavascriptInterface(new JavaExecutor(mWebView), "JavaExecutor");
Java 對象定義如下(需要特別注意的是,在 JELLY_BEAN_MR1 之后府蔗,只有 public 且添加了 @JavascriptInterface 注解的方法才能被調(diào)用)
@JavascriptInterface
public final void onJSExecutorJava(String className,String methodName,String params) throws Exception {
Class<?> targetClass = Class.forName(JSApplication.Instance().getPackageName()+ "." + className);
HashMap<String, Method> extendsMethods = getAllMethod(targetClass);;
if (extendsMethods.containsKey(methodName)) {
Method method = extendsMethods.get(methodName);
if (method != null) {
method.invoke(null, new JSONObject(params));
}
}
}
private static HashMap<String, Method> getAllMethod(Class injectedCls) throws Exception{
HashMap<String, Method> mMethodsMap = new HashMap<>();
Method[] methods = injectedCls.getDeclaredMethods();
for (Method method : methods){
String name;
if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null){
continue;
}
Class[] parameters = method.getParameterTypes();
if (null != parameters && parameters.length == 1){
if (parameters[0] == JSONObject.class) {
mMethodsMap.put(name, method);
}
}
}
return mMethodsMap;
}
所以JS可以調(diào)用的就是JavaExecutor類中的onJSExecutorJava方法晋控,這個方法我定義了三個參數(shù),分別是調(diào)用類姓赤,方法與傳遞過來的參數(shù)(主要定義為json類型的格式)
接下來就是看看JS里面的實現(xiàn)了
call: function (obj, method, params) {
JavaExecutor.onJSExecutorJava(obj, method ,JSON.stringify(params));
},
JSBridge.call('JSBridgeImpl','showToast',{'msg':'Hello JSBridge'});
我這里是調(diào)用了JSBridgeImpl的showToast方法赡译,傳遞的參數(shù)是'msg':'Hello JSBridge',這個方法是彈一個Toast,并且提示語為Hello JSBridge不铆,這個方法比較簡單就不貼出來了
接下來就是運行實驗
所以JS調(diào)用JAVA成功了
2.JAVA調(diào)用JS的方法
<p>
至于JAVA調(diào)用JS大致有以下幾個可用方法:
1.loadUrl方法
webView.loadUrl("javascript:scriptString"); //其中 scriptString 為 Javascript 代碼
2.在 KITKAT 之后蝌焚,又新增了一個方法:
webView.evaluateJavascript(scriptString, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
}
});//其中 scriptString 為 Javascript 代碼,ValueCallback 的用來獲取 Javascript 的執(zhí)行結果誓斥。這是一個異步調(diào)用只洒。
所以其實也是調(diào)用了JS的方法,接下來看下JS的方法
onJavaCall: function (method, params){
var targetFn = window[method(params)];
if (targetFn) {
targetFn(this);
}
},
我這里定義了兩個參數(shù)分別是調(diào)用方法與傳遞過來的參數(shù)(主要定義為json類型的格式)
接下來看下實際調(diào)用的方法
private static final String CALLBACK_JS_FORMAT = "JSBridge.onJavaCall(%s, %s);";
private void transact(final String script){
mHandler.post(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mWebView.evaluateJavascript(script, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
}
});
} else {
mWebView.loadUrl("javascript:" + script);
}
}
});
}
JSONObject object = new JSONObject();
object.put("key", "value");
object.put("key1", "value1");
transact(String.format(CALLBACK_JS_FORMAT, "jsFn2", String.valueOf(jsonObject));
所以是調(diào)用了js中的onJavaCall方法劳坑,通過onJavaCall去調(diào)用jsFn2方法并且把相關的json數(shù)據(jù)傳遞過去了
看一下我們定義的jsFn2方法
function jsFn2(res) {
title.style.background = 'red';
console.log(JSON.stringify(res))
}
這里主要把html中的標題的背景色進行了修改毕谴,并把傳遞的數(shù)據(jù)打印出來
運行如下
所以JAVA調(diào)用JS成功了
3.JS調(diào)用JAVA方法后,JAVA回調(diào)給JS
<p>
這種類型的使用場景距芬,肯定會在日常開發(fā)中會遇到涝开,所以我們可以以并非JSBridge的方式去想這個問題,普通Java相關使用場景看到會用到CallBack框仔,那么這里我們同樣使用CallBack來處理回調(diào)舀武。
對于JS和JAVA中的CallBack肯定不是同樣的類型,所以我們需要把JS中定義的CallBack生成一個ID存和,并把ID與CallBack關聯(lián)存儲下來,然后把ID傳遞給執(zhí)行JAVA方法奕剃,當JAVA方法執(zhí)行完后再通過上述的JAVA調(diào)用JS的方法調(diào)用JS中處理回調(diào)的方法,同時把CallBack的ID捐腿,與數(shù)據(jù)傳遞過來纵朋,JS通過ID獲取關聯(lián)數(shù)據(jù)中的CallBack,然后執(zhí)行CallBack的方法
所以流程如下
1.JS調(diào)用JAVA方法茄袖,傳遞需要信息操软,與CallBackID
2.JAVA方法執(zhí)行完后,調(diào)用JS的回調(diào)處理方法宪祥,通過CallBackID獲取CallBack方法
3.執(zhí)行CallBack方法
所以這里我們需要對JS調(diào)用JAVA的方法中定義的方法進行修改
@JavascriptInterface
public final void onJSExecutorJava(String className,String methodName,String params,long callbackId) throws Exception {
Class<?> targetClass = Class.forName(JSApplication.Instance().getPackageName()+ "." + className);
HashMap<String, Method> extendsMethods = getAllMethod(targetClass);
if (extendsMethods.containsKey(methodName)) {
Method method = extendsMethods.get(methodName);
if (method != null) {
JSCallBack callBack = new JSCallBack(mWebView,callbackId);
method.invoke(null, new JSONObject(params),callBack);
}
}
}
public class JSCallBack {
private static final String CALLBACK_JS_FORMAT = "JSBridge.onJSCallBack(%d, %s);";
WebView mWebView;
long mCallbackId;
public JSCallBack(WebView webView,long callbackId) {
mWebView = webView;
mCallbackId = callbackId;
}
public void onTranst(JSONObject jsonObject){
final String execJs = String.format(CALLBACK_JS_FORMAT,mCallbackId,String.valueOf(jsonObject));
new JsExecutor(mWebView).JavaExecutorJS(execJs);
}
}
這里主要是為JS執(zhí)行的JAVA方法增加了callbackId聂薪,并通過callbackId新建一個JSCallBack對象,當JS要執(zhí)行的JAVA方法執(zhí)行結束蝗羊,就可以執(zhí)行JSCallBack中的onTranst來調(diào)用loadUrl方法來執(zhí)行JS中回調(diào)處理方法
接下來看一個JS中回調(diào)處理方法
onJSCallBack: function (callbackId, params){
var callback = this.callbacks[callbackId];
callback && callback(params);
delete this.callbacks[callbackId];
},
這里是通過callbackId獲取callback藏澳,并執(zhí)行callback方法,最后刪除存儲的callback
最后看一下JS調(diào)用JAVA的方法耀找,這里也需要進行修改
call: function (obj, method, params, callback) {
var callbackId = Util.getID();
this.callbacks[callbackId] = callback;
JavaExecutor.onJSExecutorJava(obj, method ,JSON.stringify(params), callbackId);
},
JSBridge.call('JSBridgeImpl','showToast',{'msg':'Hello JSBridge'},function (res){console.log(JSON.stringify(res))})
這里是首先獲取了callbackId翔悠,并存儲下來业崖,最后將callbackId傳遞給執(zhí)行的JAVA方法
接下來就是測試一下我們執(zhí)行的方法,我們這邊是調(diào)用了showToast方法蓄愁,并將回調(diào)的數(shù)據(jù)打印出來双炕。
如下
4.JAVA調(diào)用JS方法后撮抓,JS回調(diào)給JAVA
<p>
其實JAVA調(diào)用JS方法JS回調(diào)給JAVA和前面的方法步驟是類似的
1.JAVA調(diào)用JS方法妇斤,傳遞需要信息,與CallBackID
2.JS方法執(zhí)行完后丹拯,調(diào)用JAVA的回調(diào)處理方法站超,通過CallBackID獲取CallBack方法
3.執(zhí)行CallBack方法
所以這里我們需要對JAVA調(diào)用JS的方法中定義的方法進行修改
private static final String CALLBACK_JS_FORMAT = "JSBridge.onJavaCall(%s, %s, %d);";
public void JavaExecutorJS(String method,JSONObject jsonObject,JavaCallBack callBack){
long callbackId = System.currentTimeMillis();
Commons.mJavaCallBacks.put(callbackId + "",callBack);
transact(String.format(CALLBACK_JS_FORMAT, method, String.valueOf(jsonObject), callbackId));
}
public interface JavaCallBack {
void onReceiveResult(JSONObject jsonObject);
}
這里獲取當前的時間作為callbackId,然后通過loadurl調(diào)用JSBridge的onJavaCall方法
onJavaCall: function (method, params, callbackId){
var targetFn = window[method(params,callbackId)];
if (targetFn) {
targetFn(this);
}
},
當我們調(diào)用JS方法時候會把參數(shù)和callbackId傳遞過去,當JS方法執(zhí)行完后乖酬,調(diào)用JAVA方法將返回的參數(shù)與callbackId傳遞過去就可以調(diào)用JAVA中定義的Callback方法了
JavaExecutor增加回調(diào)返回方法
@JavascriptInterface
public final void onJavaCallBack(String params,long callbackId){
try {
JavaCallBack callBack = Commons.mJavaCallBacks.get(callbackId + "");
callBack.onReceiveResult(new JSONObject(params));
} catch (JSONException e) {
e.printStackTrace();
}
}
回調(diào)后獲取callback顷编,調(diào)用callBack的onReceiveResult方法
同時JS中也要添加相關的調(diào)用方法如下
// JAVA調(diào)用js后的回調(diào)方法
onJAVACallBack: function (callbackId, params){
JavaExecutor.onJavaCallBack(JSON.stringify(params), callbackId);
},
到這里基本就完成了JAVA調(diào)用JS方法后,JS回調(diào)給JAVA的需求了
接下來看一下JAVA調(diào)用方法與JS中被調(diào)用的方法
JSONObject object = new JSONObject();
object.put("key", "value");
object.put("key1", "value1");
new JsExecutor(mWebView).JavaExecutorJS("jsFn2", getJSONObject(0, "ok", object), new JavaCallBack() {
@Override
public void onReceiveResult(JSONObject jsonObject) {
Toast.makeText(getBaseContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
}
});
function jsFn2(res,callbackId) {
title.style.background = 'red';
console.log(JSON.stringify(res))
JSBridge.onJAVACallBack(callbackId,{'msg':'Hello JSBridge'})
}
所以需要實現(xiàn)的效果是剑刑,title的背景色變化后彈出Toast
運行如下
bingo~~~完成
寫在后面的話
<p>
java 代碼與 javascript 代碼中通信通過這幾種方式都可以實現(xiàn),當然根據(jù)不同的應用和業(yè)務需求施掏,開發(fā)者們可以根據(jù)各自的場景進行相關封裝钮惠,peace~~~