日期:2014-05-16  浏览次数:20996 次

Cordova 3.x 源码分析(6) -- cordova.js本地交互JS<->Native
src/android/android/nativeapiprovider.js JS->Native的具体交互形式
// file: src/android/android/nativeapiprovider.js
define("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.js
define("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.js
define("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.