Cordova-Android消息传递方式

JS向Native传递消息的方式

  1. prompt
    即通过js弹出窗口的形式进行消息传递,Android通过webChromeClient.onJsPrompt拦截对应的prompt的消息来接收来之js的数据。例如:
    js端:
    prompt(value, 'gap_bridge_mode:' + bridgeSecret);
    
    Android端:
    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;
    }
    
  2. JsObject
    即通过Android暴露给js的_cordovaNative对象进行消息传递
    js端:
    _cordovaNative.exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
    
    Android端:
    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传递消息的方式

  1. js主动发起消息,Android被动将消息返回
    Android不会主动向js发送消息,也不会通知js来取消息,js主动通过上方JS向Native传递消息的方式来拉去Android的消息队列中的消息。
  2. 通过webview.loadUrl的方式
    Android通过webview.loadUrl("javascript:cordova.callbackFromNative(callBackId,true,'01','[]',false)")的方式直接调用js端的callbackFromNative()方法
  3. 上/下线事件
    通过webView.setNetworkAvailable(value)通知js来拉取Android消息队列的消息,只要检测到网络上下线即过来消息
  4. 通过evaluateJavascript方式
    通过webView.evaluateJavascript(js, callback);,Android4.4以上才提供的API,可以直接执行一个js中的方法,因此能主动将消息推送给js端。

解决了jsAndroid双向消息互传之后需要便需要定义双方消息传递的的规范了,否则js发送一串消息给Android,Android却不理解你说的话,那么Android就无法执行对应的操作了~

js传消息给Android的规则:

首先需要商量好Android的接收模式,并确定好secret、然后按照接收模式传递:

  1. secret
    双方协商的秘钥,有了秘钥,Android就能够判断是否有人冒充js在给他发送消息了,这时候Android就会拒绝执行相应操作。
    打个比方:B向A发送聊天请求,A判断B是其可以信赖的人,因此A将一个密码给B,每次B给A发送消息的时候带上了密码,A就能知道是B在发送消息了,但是这时候C也向A发送聊天请求,A判断C不是可信赖的,因此不给其密码,因此C发送给A的消息因为全部都没有密码而被A全部拒收了。
  2. callBackId
    回调方法的id
    js调用Android的方法,可能同一个方法被执行多次,因此需要一个callBackId确定是哪次、哪个方法对应的回调。(jsonp也需要有个callBackId
  3. Service
    告诉Android,我需要调用你的哪个服务,js中对应的Service并未一个完整的包名的Android类、但是Android中通过config.xml中的配置能找到Service对应的Android的完整包名,因此能通过Java的反射机制,得到一个Android的实体类的实例
  4. action
    告诉Android端,我需要调用你的具体的方法
  5. args:
    传递给Android端方法的参数列表

通过Serviceaction以及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;
    }
  1. 参数格式:
    js传递给Android的参数格式固定为JSONArray格式的字符串,因此可以直接通过以下方式进行解析:
    JSONArray args = new JSONArray(rawArgs);
    

Android传递消息给js

Cordova-Android设计了一种优秀的传递消息给js的方式,其定义了一套完整的消息协议,协议包含了以下内容:

cordova-android-消息协议解析.png
  1. 执行结果
    执行结果包含两种状态:
    • S: 成功:状态为NO_RESULTOK
    • F: 失败:其他状态
  2. 状态码
    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
    }
    
  3. callbackId
    callbackId与每一个cordova.exec()存在一一对应关系,通过callbackId就能找到cordova.exec()对应的回调方法了。
  4. 消息的类型
    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方式解析
  5. 是否释放回调函数
    大多数情况下,cordova.exec()方法拿到回调结果即可删除了,但是Cordova-Android的cordova.exec()执行之后,有时候并不仅仅只希望收到一次回调消息,例如CoreAndroid对应的messageChannel便需要能多次收到回调的消息。此时keepCallback发挥作用了,其使js端在收到了回调消息之后并不会将callbackId对应的回调方法删除,因此下次Android发送了对应callbackId的消息的时候js端依旧能找到其对应的回调方法并执行。
  6. 消息长度
    消息长度的存在使得Android的消息批量发送给js成为可能,消息长度标识了从消息长度对应的字符串后面空格之后的第一个字符串开始,一直往后数消息长度的字符,这部分即为一条完整的消息,其余部分可能是*也可能是另外一条完整的消息,也可能是一个消息的集合。此时我只需要对消息长度范围内的字符串当做一条消息处理即可,其余部分等待下次再行处理。因此理论上无论多少条消息,都能通过消息长度来进行分割了。
  7. 其他
    Cordova-Android中存在特殊的字符*,在Android队列中的消息数目条数过多,且数据量过大,无法打包一次性将消息发送给js端时,将会在消息的末尾添加一个*号(其为“消息长度"对应字符串的范围之外的第一个),其存在告诉js:“消息太多了,我还没发完,你再自己来取取看,可能还有消息需要你自取”,当然由于NativeToJs发送消息的模式的不同,这部分剩余的消息可能会被Android主动发送给js端,因此就算是某个消息末尾之外为*,js去取消息也不一定能取到消息,算是一个保险设置吧。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容