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去取消息也不一定能取到消息,算是一个保险设置吧。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容