src/android/android/nativeapiprovider.js JS->Native的具体交互形式
// file: src/android/android/nativeapiprovider.jsdefine("cordova/android/nativeapiprovider", function(require, exports, module) {// WebView中是否通过addJavascriptInterface提供了访问ExposedJsApi.java的_cordovaNative对象// 如果不存在选择prompt()形式的交互方式var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');var currentApi = nativeApi;module.exports = {    // 获取当前交互方式    get: function() { return currentApi; },    // 设置使用prompt()交互方式    // (true:prompt false:自动选择)    setPreferPrompt: function(value) {        currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi;    },    // 直接设置交互方式对象(很少用到)    set: function(value) {        currentApi = value;    }};});


src/android/android/promptbasednativeapi.js 通过prompt()和Native交互(Android2.3 simulator的Bug)
// file: src/android/android/promptbasednativeapi.jsdefine("cordova/android/promptbasednativeapi", function(require, exports, module) {// 由于Android2.3模拟器存在Bug,不支持addJavascriptInterface()// 所以借助prompt()来和Native进行交互// Native端会在CordovaChromeClient.onJsPrompt()中拦截处理module.exports = {    // 调用Native API    exec: function(service, action, callbackId, argsJson) {        return prompt(argsJson, 'gap:'+JSON.stringify([service, action, callbackId]));    },    // 设置Native->JS的桥接模式    setNativeToJsBridgeMode: function(value) {        prompt(value, 'gap_bridge_mode:');    },    // 接收消息    retrieveJsMessages: function(fromOnlineEvent) {        return prompt(+fromOnlineEvent, 'gap_poll:');    }};});


src/android/exec.js 执行JS->Native交互
// file: src/android/exec.jsdefine("cordova/exec", function(require, exports, module) {var cordova = require('cordova'),    nativeApiProvider = require('cordova/android/nativeapiprovider'),    utils = require('cordova/utils'),    base64 = require('cordova/base64'),    // JS->Native的可选交互形式一览    jsToNativeModes = {        // 基于prompt()的交互         PROMPT: 0,        // 基于JavascriptInterface的交互         JS_OBJECT: 1,        // 基于URL的交互         // ***由于安全问题,默认已经设置成不可用的!!!        // NativeToJsMessageQueue.ENABLE_LOCATION_CHANGE_EXEC_MODE=false        LOCATION_CHANGE: 2    },    // Native->JS的可选交互形式一览    nativeToJsModes = {        // 轮询(JS->Native自助获取消息)        POLLING: 0,        // 使用 webView.loadUrl("javascript:") 来执行消息        // 解决软键盘的Bug        LOAD_URL: 1,        // 拦截事件监听,使用online/offline事件来告诉JS获取消息        // 默认值 NativeToJsMessageQueue.DEFAULT_BRIDGE_MODE=2        ONLINE_EVENT: 2,        // 反射Webview的私有API来执行JS(需要Android 3.2.4以上版本)        PRIVATE_API: 3    },    // 当前JS->Native的交互形式    jsToNativeBridgeMode,    // 当前Native->JS的交互形式    nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,    pollEnabled = false,    messagesFromNative = [];// 执行Cordova提供的API// 比如: exec(successCallback, errorCallback, "Camera", "takePicture", args);function androidExec(success, fail, service, action, args) {    // 默认采用JavascriptInterface交互方式    if (jsToNativeBridgeMode === undefined) {        androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);    }    // 如果参数中存在ArrayBuffer类型的参数,转化成字符串    for (var i = 0; i < args.length; i++) {        if (utils.typeName(args[i]) == 'ArrayBuffer') {            args[i] = base64.fromArrayBuffer(args[i]);        }    }    var callbackId = service + cordova.callbackId++,        // 把所有参数转换成JSON串        argsJson = JSON.stringify(args);    // 设置回调函数    if (success || fail) {        cordova.callbacks[callbackId] = {success:success, fail:fail};    }    if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {        // 基于URL的交互(需要手动修改NativeToJsMessageQueue.java的常量配置才能起效)        // Native端会在CordovaWebViewClient.shouldOverrideUrlLoading()中拦截处理        window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;    } else {        // 选择合适的交互方式和Native进行交互        // 根据Native端NativeToJsMessageQueue.DISABLE_EXEC_CHAINING的配置,回传消息可以是同步或者异步        // 默认是同步的,返回PluginResult对象的JSON串。异步的话messages为空。        var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);        if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {            // 如果参数被传递到Java端,但是接收到的是null,切换交互方式到prompt()在执行一次            // Galaxy S2在传递某些Unicode字符的时候少数情况下有问题,            // 参考 https://issues.apache.org/jira/browse/CB-2666            androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);            androidExec(success, fail, service, action, args);            // 执行完成后,把交互方式再切回JavascriptInterface            androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);            return;        } else {            // 处理Native返回的消息            androidExec.processMessages(messages);        }    }}function pollOnceFromOnlineEvent() {    pollOnce(true);}// 从Native的消息队列中获取消息function pollOnce(opt_fromOnlineEvent) {    var msg = nativeApiProvider.get().retrieveJsMessages(!!opt_fromOnlineEvent);    androidExec.processMessages(msg);}function pollingTimerFunc() {    if (pollEnabled) {        pollOnce();        setTimeout(pollingTimerFunc, 50);    }}function hookOnlineApis() {    function proxyEvent(e) {        cordova.fireWindowEvent(e.type);    }    window.addEventListener('online', pollOnceFromOnlineEvent, false);    window.addEventListener('offline', pollOnceFromOnlineEvent, false);    cordova.addWindowEventHandler('online');    cordova.addWindowEventHandler('offline');    document.addEventListener('online', proxyEvent, false);    document.addEventListener('offline', proxyEvent, false);}// 添加online/offline事件hookOnlineApis();// 外部可以访问到交互方式的常量androidExec.jsToNativeModes = jsToNativeModes;androidExec.nativeToJsModes = nativeToJsModes;// 设置JS->Native的交互方式androidExec.setJsToNativeBridgeMode = function(mode) {    // JavascriptInterface方式但是Native无法提供_cordovaNative对象的时候强制切到prompt()    if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaNative) {        console.log('Falling back on PROMPT mode since _cordovaNative is missing. Expected for Android 3.2 and lower only.');        mode = jsToNativeModes.PROMPT;    }    nativeApiProvider.setPreferPrompt(mode == jsToNativeModes.PROMPT);    jsToNativeBridgeMode = mode;};// 设置Native->JS的交互方式androidExec.setNativeToJsBridgeMode = function(mode) {    if (mode == nativeToJsBridgeMode) {        return;    }    // 如果以前是Poll的方式,,先回置到非Poll    if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {        pollEnabled = false;    }    nativeToJsBridgeMode = mode;    // 告诉Native端,JS端获取消息的方式    nativeApiProvider.get().setNativeToJsBridgeMode(mode);·   // 如果是在JS端Poll的方式的话    if (mode == nativeToJsModes.POLLING) {        pollEnabled = true;        // 停顿后执行exec获取消息message        setTimeout(pollingTimerFunc, 1);    }};// 处理从Native返回的一条消息// // 回传消息的完整格式:// (1)消息的长度+空格+J+JavaScript代码// 44 Jcordova.callbackFromNative('InAppBrowser1478332075',true,1,[{"type":"loadstop","url":"http:\/\/www.baidu.com\/"}],true});// (2)消息的长度+空格+成功失败标示(J/S/F)+keepCallback标示+具体的状态码+空格+回调ID+空格+回传数据// 78 S11 InAppBrowser970748887 {"type":"loadstop","url":"http:\/\/www.baidu.com\/"}// 28 S01 Notification970748888 n0//// 默认是关闭了返回Js代码,使用返回数据。// NativeToJsMessageQueue.FORCE_ENCODE_USING_EVAL=falsefunction processMessage(message) {    try {        var firstChar = message.charAt(0);        if (firstChar == 'J') {            // 执行回传的JavaScript代码            eval(message.slice(1));        } else if (firstChar == 'S' || firstChar == 'F') {            // S代表处理成功(包含没有数据),F代表处理失败            var success = firstChar == 'S';            var keepCallback = message.charAt(1) == '1';            var spaceIdx = message.indexOf(' ', 2);            var status = +message.slice(2, spaceIdx);            var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);            var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);            // 回传值类型            var payloadKind = message.charAt(nextSpaceIdx + 1);            var payload;            if (payloadKind == 's') {                // 字符串:s+字符串                payload = message.slice(nextSpaceIdx + 2);            } else if (payloadKind == 't') {                // 布尔值:t/f                payload = true;            } else if (payloadKind == 'f') {                // 布尔值:t/f                payload = false;            } else if (payloadKind == 'N') {                // Null:N                payload = null;            } else if (payloadKind == 'n') {                // 数值:n+具体值                payload = +message.slice(nextSpaceIdx + 2);            } else if (payloadKind == 'A') {                // ArrayBuffer:A+数据                var data = message.slice(nextSpaceIdx + 2);                var bytes = window.atob(data);                var arraybuffer = new Uint8Array(bytes.length);                for (var i = 0; i < bytes.length; i++) {                    arraybuffer[i] = bytes.charCodeAt(i);                }                payload = arraybuffer.buffer;            } else if (payloadKind == 'S') {                // 二进制字符串:S+字符串                payload = window.atob(message.slice(nextSpaceIdx + 2));            } else {                // JSON:JSON串                payload = JSON.parse(message.slice(nextSpaceIdx + 1));            }            // 调用回调函数            cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);        } else {            console.log("processMessage failed: invalid message:" + message);        }    } catch (e) {        console.log("processMessage failed: Message: " + message);        console.log("processMessage failed: Error: " + e);        console.log("processMessage failed: Stack: " + e.stack);    }}// 处理Native返回的消息androidExec.processMessages = function(messages) {    // 如果消息存在的话(异步没有直接返回)    if (messages) {        // 把传入的消息放到数组中        messagesFromNative.push(messages);        // messagesFromNative是全局的,而processMessages方法可重入,        // 所以只需放到数组,后边循环处理即可        if (messagesFromNative.length > 1) {            return;        }        // 遍历从Native获得所有消息        while (messagesFromNative.length) {            // 处理完成后才可以从数组中删除            messages = messagesFromNative[0];            // Native返回星号代表消息需要等一会儿再取            if (messages == '*') {                // 删除数组的第一个元素                messagesFromNative.shift();                // 再次去获取消息                window.setTimeout(pollOnce, 0);                return;            }            // 获取消息的长度            var spaceIdx = messages.indexOf(' ');            var msgLen = +messages.slice(0, spaceIdx);            // 获取第一个消息            var message = messages.substr(spaceIdx + 1, msgLen);            // 截取掉第一个消息            messages = messages.slice(spaceIdx + msgLen + 1);            // 处理第一个消息            processMessage(message);            // 如果消息包含多个,继续处理;单个的话删除本地消息数组中的数据            if (messages) {                messagesFromNative[0] = messages;            } else {                messagesFromNative.shift();            }        }    }};module.exports = androidExec;});

更多相关文章

  1. AndroidAndroid程序提示和消息button响应事件
  2. android调用系统自带资源的两种方式
  3. android TabHost导航切换实现方式
  4. Android 实现动画方式
  5. android studio 在线更新android sdk,遇到无法Fetching https://d
  6. Android消息循环
  7. android http 请求方式
  8. Android 高级进阶之深入剖析消息机制
  9. MVC 5限制所有HTTP请求必须是POST方式

随机推荐

  1. Android模仿通讯录
  2. Android(安卓)Studio 配置模拟器AVD存放
  3. 使用android快速开发框架afinal的FinalDb
  4. Android如何连接和操作SQLite数据库
  5. JavaME无法满足Android,只有设计 Dalvik
  6. Android(安卓)性能优化之使用MAT分析内存
  7. Android实现数据存储技术
  8. android service
  9. android启动加速
  10. 如何在电脑上安装模拟器体验Android(安卓