JS向Native传递消息的方式
- prompt
即通过js弹出窗口的形式进行消息传递,Android通过webChromeClient.onJsPrompt
拦截对应的prompt
的消息来接收来之js的数据。例如:
js端:
Android端:prompt(value, 'gap_bridge_mode:' + bridgeSecret);
public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result) { if(message.startWith("gap_bridge_mode:"){ // do something with the defaultValue and origin result.confirm("Some results"); }else{ dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() { @Override public void gotResult(boolean success, String value) { if (success) { result.confirm(value); } else { result.cancel(); } } }); } return true; }
- JsObject
即通过Android暴露给js的_cordovaNative
对象进行消息传递
js端:
Android端:_cordovaNative.exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
class SystemExposedJsApi implements ExposedJsApi { private final CordovaBridge bridge; SystemExposedJsApi(CordovaBridge bridge) { this.bridge = bridge; } .... @JavascriptInterface public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments); } .... } SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge); webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
Native向js传递消息的方式
- js主动发起消息,Android被动将消息返回
Android不会主动向js发送消息,也不会通知js来取消息,js主动通过上方JS向Native传递消息的方式
来拉去Android的消息队列中的消息。 - 通过
webview.loadUrl
的方式
Android通过webview.loadUrl("javascript:cordova.callbackFromNative(callBackId,true,'01','[]',false)")
的方式直接调用js端的callbackFromNative()
方法 - 上/下线事件
通过webView.setNetworkAvailable(value)
来通知js来拉取Android消息队列的消息,只要检测到网络上下线即过来取消息 - 通过
evaluateJavascript
方式
通过webView.evaluateJavascript(js, callback);
,Android4.4以上才提供的API,可以直接执行一个js中的方法,因此能主动将消息推送给js端。
解决了js
与Android
双向消息互传之后需要便需要定义双方消息传递的的规范了,否则js发送一串消息给Android,Android却不理解你说的话,那么Android就无法执行对应的操作了~
js传消息给Android的规则:
首先需要商量好Android的接收模式,并确定好secret
、然后按照接收模式传递:
-
secret
双方协商的秘钥,有了秘钥,Android就能够判断是否有人冒充js在给他发送消息了,这时候Android就会拒绝执行相应操作。
打个比方:B向A发送聊天请求,A判断B是其可以信赖的人,因此A将一个密码给B,每次B给A发送消息的时候带上了密码,A就能知道是B在发送消息了,但是这时候C也向A发送聊天请求,A判断C不是可信赖的,因此不给其密码,因此C发送给A的消息因为全部都没有密码而被A全部拒收了。 -
callBackId
回调方法的id
js调用Android的方法,可能同一个方法被执行多次,因此需要一个callBackId确定是哪次、哪个方法对应的回调。(jsonp也需要有个callBackId) -
Service
告诉Android,我需要调用你的哪个服务,js中对应的Service
并未一个完整的包名的Android类、但是Android中通过config.xml
中的配置能找到Service
对应的Android的完整包名,因此能通过Java的反射机制,得到一个Android的实体类的实例 - action
告诉Android端,我需要调用你的具体的方法 - args:
传递给Android端方法的参数列表
通过Service
、action
以及Java的反射机制,能够确定并得到一个Android中的实例的方法,有了args
就能执行该方法了。
private CordovaPlugin instantiatePlugin(String className) {
CordovaPlugin ret = null;
try {
Class<?> c = null;
if ((className != null) && !("".equals(className))) {
c = Class.forName(className);
}
if (c != null & CordovaPlugin.class.isAssignableFrom(c)) {
ret = (CordovaPlugin) c.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error adding plugin " + className + ".");
}
return ret;
}
- 参数格式:
js传递给Android的参数格式固定为JSONArray格式的字符串,因此可以直接通过以下方式进行解析:JSONArray args = new JSONArray(rawArgs);
Android传递消息给js
Cordova-Android设计了一种优秀的传递消息给js的方式,其定义了一套完整的消息协议,协议包含了以下内容:
- 执行结果
执行结果包含两种状态:- S: 成功:状态为
NO_RESULT
或OK
- F: 失败:其他状态
- S: 成功:状态为
- 状态码
Cordova-Android统共定义了10种状态,但是其中某些状态在CordovaLib种并未被用到,可能是“留待备用”,开始时也曾怀疑10种状态是否足够?但是后来觉得,其实错误主要关注的的是错误对应的错误消息,有了错误消息即使只有一种错误码,也足够让使用者快速定位出错的原因了,而且错误消息的内容未尝不能包含错误码:public static String[] StatusMessages = new String[] { "No result", "OK", "Class not found", "Illegal access", "Instantiation error", "Malformed url", "IO error", "Invalid action", "JSON error", "Error" }; public enum Status { NO_RESULT, OK, CLASS_NOT_FOUND_EXCEPTION, ILLEGAL_ACCESS_EXCEPTION, INSTANTIATION_EXCEPTION, MALFORMED_URL_EXCEPTION, IO_EXCEPTION, INVALID_ACTION, JSON_EXCEPTION, ERROR }
-
callbackId
callbackId
与每一个cordova.exec()
存在一一对应关系,通过callbackId
就能找到cordova.exec()
对应的回调方法了。 - 消息的类型
Android向js端传送的消息有8种类型,对应为:序号 名称 代码 解释 1 Boolean t|f 布尔值,类型本身即包含了结果 2 String s 字符串类型 3 Number n 数字类型 4 Null N 空 5 ArrayBuffer A ArrayBuffer类型 6 BinarySting S 二进制字符串 7 Multipart M 多个Result的组合,解析时将被拆分为多条消息 8 Json 无 能被json解析,代码位为非其他的所有类型时将使用json方式解析 - 是否释放回调函数
大多数情况下,cordova.exec()
方法拿到回调结果即可删除了,但是Cordova-Android的cordova.exec()
执行之后,有时候并不仅仅只希望收到一次回调消息,例如CoreAndroid
对应的messageChannel
便需要能多次收到回调的消息。此时keepCallback
发挥作用了,其使js端在收到了回调消息之后并不会将callbackId
对应的回调方法删除,因此下次Android发送了对应callbackId
的消息的时候js端依旧能找到其对应的回调方法并执行。 - 消息长度
消息长度的存在使得Android的消息批量发送给js成为可能,消息长度标识了从消息长度对应的字符串后面空格之后的第一个字符串开始,一直往后数消息长度的字符,这部分即为一条完整的消息,其余部分可能是*
也可能是另外一条完整的消息,也可能是一个消息的集合。此时我只需要对消息长度范围内的字符串当做一条消息处理即可,其余部分等待下次再行处理。因此理论上无论多少条消息,都能通过消息长度来进行分割了。 - 其他
Cordova-Android中存在特殊的字符*
,在Android队列中的消息数目条数过多,且数据量过大,无法打包一次性将消息发送给js端时,将会在消息的末尾添加一个*
号(其为“消息长度"对应字符串的范围之外的第一个),其存在告诉js:“消息太多了,我还没发完,你再自己来取取看,可能还有消息需要你自取”,当然由于NativeToJs发送消息的模式的不同,这部分剩余的消息可能会被Android主动发送给js端,因此就算是某个消息末尾之外为*
,js去取消息也不一定能取到消息,算是一个保险设置吧。