Js调用android底层,【Android】实现一个JSBridge

为什么使用JSBridge

在平时业务的开发中,为了追求开发的效率以及移植的便利性,一些展示性强的页面我们会偏向于使用h5来完成,功能性强的页面我们会偏向于使用native来完成,而一旦使用了h5,为了在h5中尽可能的得到native的体验,我们native层需要暴露一些方法给js调用,比如,弹Toast提醒,弹Dialog,分享等等,同样我们也可以通过native方法去直接调用js的方法,所以 JsBridge 就是用来在 Android app的原生 java 代码与 javascript 代码中架设通信(调用)桥梁的辅助工具。

这里我们分析一下JSBridge的使用场景

1.JS调用JAVA的方法

2.JAVA调用JS的方法

3.JS调用JAVA方法后,JAVA回调给JS

4.JAVA调用JS方法后,JS回调给JAVA

所以针对上面的使用场景,我们分别来实现

1.JS调用JAVA的方法

WebView 提供了一个接口,可以让我们注入 Java 对象到页面中,这样,页面中的 javascript 就能直接访问 Java 对象的接口,从而实现 Java 和 Javascript 的交互。

首先必须启用 WebView 中的 Javascript 支持

mWebView.getSettings().setJavaScriptEnabled(true);

注入 Java 对象到 WebView 中

mWebView.addJavascriptInterface(new JavaExecutor(mWebView), "JavaExecutor");

Java 对象定义如下(需要特别注意的是,在 JELLY_BEAN_MR1 之后,只有 public 且添加了 @JavascriptInterface 注解的方法才能被调用)

@JavascriptInterface

public final void onJSExecutorJava(String className,String methodName,String params) throws Exception {

Class> targetClass = Class.forName(JSApplication.Instance().getPackageName()+ "." + className);

HashMap extendsMethods = getAllMethod(targetClass);;

if (extendsMethods.containsKey(methodName)) {

Method method = extendsMethods.get(methodName);

if (method != null) {

method.invoke(null, new JSONObject(params));

}

}

}

private static HashMap getAllMethod(Class injectedCls) throws Exception{

HashMap 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可以调用的就是JavaExecutor类中的onJSExecutorJava方法,这个方法我定义了三个参数,分别是调用类,方法与传递过来的参数(主要定义为json类型的格式)

接下来就是看看JS里面的实现了

call: function (obj, method, params) {

JavaExecutor.onJSExecutorJava(obj, method ,JSON.stringify(params));

},

JSBridge.call('JSBridgeImpl','showToast',{'msg':'Hello JSBridge'});

我这里是调用了JSBridgeImpl的showToast方法,传递的参数是'msg':'Hello JSBridge',这个方法是弹一个Toast,并且提示语为Hello JSBridge,这个方法比较简单就不贴出来了

接下来就是运行实验

1e4f79ec5bc6

图1 Js调用Java方法

所以JS调用JAVA成功了

2.JAVA调用JS的方法

至于JAVA调用JS大致有以下几个可用方法:

1.loadUrl方法

webView.loadUrl("javascript:scriptString"); //其中 scriptString 为 Javascript 代码

2.在 KITKAT 之后,又新增了一个方法:

webView.evaluateJavascript(scriptString, new ValueCallback() {

@Override

public void onReceiveValue(String value) {

}

});//其中 scriptString 为 Javascript 代码,ValueCallback 的用来获取 Javascript 的执行结果。这是一个异步调用。

所以其实也是调用了JS的方法,接下来看下JS的方法

onJavaCall: function (method, params){

var targetFn = window[method(params)];

if (targetFn) {

targetFn(this);

}

},

我这里定义了两个参数分别是调用方法与传递过来的参数(主要定义为json类型的格式)

接下来看下实际调用的方法

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() {

@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));

所以是调用了js中的onJavaCall方法,通过onJavaCall去调用jsFn2方法并且把相关的json数据传递过去了

看一下我们定义的jsFn2方法

function jsFn2(res) {

title.style.background = 'red';

console.log(JSON.stringify(res))

}

这里主要把html中的标题的背景色进行了修改,并把传递的数据打印出来

运行如下

1e4f79ec5bc6

图2 Java调用Js方法一

1e4f79ec5bc6

图3 Java调用Js方法二

所以JAVA调用JS成功了

3.JS调用JAVA方法后,JAVA回调给JS

这种类型的使用场景,肯定会在日常开发中会遇到,所以我们可以以并非JSBridge的方式去想这个问题,普通Java相关使用场景看到会用到CallBack,那么这里我们同样使用CallBack来处理回调。

对于JS和JAVA中的CallBack肯定不是同样的类型,所以我们需要把JS中定义的CallBack生成一个ID,并把ID与CallBack关联存储下来,然后把ID传递给执行JAVA方法,当JAVA方法执行完后再通过上述的JAVA调用JS的方法调用JS中处理回调的方法,同时把CallBack的ID,与数据传递过来,JS通过ID获取关联数据中的CallBack,然后执行CallBack的方法

所以流程如下

1.JS调用JAVA方法,传递需要信息,与CallBackID

2.JAVA方法执行完后,调用JS的回调处理方法,通过CallBackID获取CallBack方法

3.执行CallBack方法

所以这里我们需要对JS调用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 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执行的JAVA方法增加了callbackId,并通过callbackId新建一个JSCallBack对象,当JS要执行的JAVA方法执行结束,就可以执行JSCallBack中的onTranst来调用loadUrl方法来执行JS中回调处理方法

接下来看一个JS中回调处理方法

onJSCallBack: function (callbackId, params){

var callback = this.callbacks[callbackId];

callback && callback(params);

delete this.callbacks[callbackId];

},

这里是通过callbackId获取callback,并执行callback方法,最后删除存储的callback

最后看一下JS调用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传递给执行的JAVA方法

接下来就是测试一下我们执行的方法,我们这边是调用了showToast方法,并将回调的数据打印出来。

如下

1e4f79ec5bc6

图4 Js调用Java方法

1e4f79ec5bc6

图5 Js调用Java方法,回调打印结果

4.JAVA调用JS方法后,JS回调给JAVA

其实JAVA调用JS方法JS回调给JAVA和前面的方法步骤是类似的

1.JAVA调用JS方法,传递需要信息,与CallBackID

2.JS方法执行完后,调用JAVA的回调处理方法,通过CallBackID获取CallBack方法

3.执行CallBack方法

所以这里我们需要对JAVA调用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调用JSBridge的onJavaCall方法

onJavaCall: function (method, params, callbackId){

var targetFn = window[method(params,callbackId)];

if (targetFn) {

targetFn(this);

}

},

当我们调用JS方法时候会把参数和callbackId传递过去,当JS方法执行完后,调用JAVA方法将返回的参数与callbackId传递过去就可以调用JAVA中定义的Callback方法了

JavaExecutor增加回调返回方法

@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();

}

}

回调后获取callback,调用callBack的onReceiveResult方法

同时JS中也要添加相关的调用方法如下

// JAVA调用js后的回调方法

onJAVACallBack: function (callbackId, params){

JavaExecutor.onJavaCallBack(JSON.stringify(params), callbackId);

},

到这里基本就完成了JAVA调用JS方法后,JS回调给JAVA的需求了

接下来看一下JAVA调用方法与JS中被调用的方法

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'})

}

所以需要实现的效果是,title的背景色变化后弹出Toast

运行如下

1e4f79ec5bc6

图6JAVA调用JS方法后,JS回调给JAVA

bingo~~~完成

写在后面的话

java 代码与 javascript 代码中通信通过这几种方式都可以实现,当然根据不同的应用和业务需求,开发者们可以根据各自的场景进行相关封装,peace~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值