在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 嵌入式/ cordova.js 本地交互 JS<->Native
Cordova CLI
CordovaLib 概要
cordova.js 概要
cordova.js 事件通道 pub/sub
cordova.js 模塊系統(tǒng) require/define
cordova.js 導(dǎo)入、初始化、啟動(dòng)、加載插件
cordova.js 本地交互 JS<->Native

cordova.js 本地交互 JS<->Native

src/android/android/nativeapiprovider.js JS->Native 的具體交互形式

Js 代碼

// file: src/android/android/nativeapiprovider.js
define("cordova/android/nativeapiprovider", function(require, exports, module) {

// WebView中是否通過(guò)addJavascriptInterface提供了訪問(wèn)ExposedJsApi.java的_cordovaNative對(duì)象
// 如果不存在選擇prompt()形式的交互方式
var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');
var currentApi = nativeApi;

module.exports = {
    // 獲取當(dāng)前交互方式
    get: function() { return currentApi; },

    // 設(shè)置使用prompt()交互方式
    // (true:prompt false:自動(dòng)選擇)
    setPreferPrompt: function(value) {
        currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi;
    },

    // 直接設(shè)置交互方式對(duì)象(很少用到)
    set: function(value) {
        currentApi = value;
    }
};

});

src/android/android/promptbasednativeapi.js 通過(guò) prompt()和 Native 交互(Android2.3 simulator 的 Bug)

Js 代碼

// file: src/android/android/promptbasednativeapi.js
define("cordova/android/promptbasednativeapi", function(require, exports, module) {

// 由于Android2.3模擬器存在Bug,不支持addJavascriptInterface()
// 所以借助prompt()來(lái)和Native進(jìn)行交互
// Native端會(huì)在CordovaChromeClient.onJsPrompt()中攔截處理
module.exports = {
    // 調(diào)用Native API
    exec: function(service, action, callbackId, argsJson) {
        return prompt(argsJson, 'gap:'+JSON.stringify([service, action, callbackId]));
    },

    // 設(shè)置Native->JS的橋接模式
    setNativeToJsBridgeMode: function(value) {
        prompt(value, 'gap_bridge_mode:');
    },

    // 接收消息
    retrieveJsMessages: function(fromOnlineEvent) {
        return prompt(+fromOnlineEvent, 'gap_poll:');
    }
};

});

src/android/exec.js 執(zhí)行 JS->Native 交互

Js 代碼

// 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的交互 
        // ***由于安全問(wèn)題,默認(rèn)已經(jīng)設(shè)置成不可用的?。。?        // NativeToJsMessageQueue.ENABLE_LOCATION_CHANGE_EXEC_MODE=false
        LOCATION_CHANGE: 2
    },

    // Native->JS的可選交互形式一覽
    nativeToJsModes = {
        // 輪詢(JS->Native自助獲取消息)
        POLLING: 0,
        // 使用 webView.loadUrl("javascript:") 來(lái)執(zhí)行消息
        // 解決軟鍵盤的Bug
        LOAD_URL: 1,
        // 攔截事件監(jiān)聽(tīng),使用online/offline事件來(lái)告訴JS獲取消息
        // 默認(rèn)值 NativeToJsMessageQueue.DEFAULT_BRIDGE_MODE=2
        ONLINE_EVENT: 2,
        // 反射Webview的私有API來(lái)執(zhí)行JS(需要Android 3.2.4以上版本)
        PRIVATE_API: 3
    },

    // 當(dāng)前JS->Native的交互形式
    jsToNativeBridgeMode,
    // 當(dāng)前Native->JS的交互形式
    nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT,
    pollEnabled = false,
    messagesFromNative = [];

// 執(zhí)行Cordova提供的API
// 比如: exec(successCallback, errorCallback, "Camera", "takePicture", args);
function androidExec(success, fail, service, action, args) {
    // 默認(rèn)采用JavascriptInterface交互方式
    if (jsToNativeBridgeMode === undefined) {
        androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
    }

    // 如果參數(shù)中存在ArrayBuffer類型的參數(shù),轉(zhuǎn)化成字符串
    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++,
        // 把所有參數(shù)轉(zhuǎn)換成JSON串
        argsJson = JSON.stringify(args);

    // 設(shè)置回調(diào)函數(shù)
    if (success || fail) {
        cordova.callbacks[callbackId] = {success:success, fail:fail};
    }

    if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
        // 基于URL的交互(需要手動(dòng)修改NativeToJsMessageQueue.java的常量配置才能起效)
        // Native端會(huì)在CordovaWebViewClient.shouldOverrideUrlLoading()中攔截處理
        window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
    } else {
        // 選擇合適的交互方式和Native進(jìn)行交互
        // 根據(jù)Native端NativeToJsMessageQueue.DISABLE_EXEC_CHAINING的配置,回傳消息可以是同步或者異步
        // 默認(rèn)是同步的,返回PluginResult對(duì)象的JSON串。異步的話messages為空。
        var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);

        if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
            // 如果參數(shù)被傳遞到Java端,但是接收到的是null,切換交互方式到prompt()在執(zhí)行一次
            // Galaxy S2在傳遞某些Unicode字符的時(shí)候少數(shù)情況下有問(wèn)題,
            // 參考 https://issues.apache.org/jira/browse/CB-2666
            androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
            androidExec(success, fail, service, action, args);

            // 執(zhí)行完成后,把交互方式再切回JavascriptInterface
            androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
            return;
        } else {
            // 處理Native返回的消息
            androidExec.processMessages(messages);
        }
    }
}

function pollOnceFromOnlineEvent() {
    pollOnce(true);
}

// 從Native的消息隊(duì)列中獲取消息
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();

// 外部可以訪問(wèn)到交互方式的常量
androidExec.jsToNativeModes = jsToNativeModes;
androidExec.nativeToJsModes = nativeToJsModes;

// 設(shè)置JS->Native的交互方式
androidExec.setJsToNativeBridgeMode = function(mode) {
    // JavascriptInterface方式但是Native無(wú)法提供_cordovaNative對(duì)象的時(shí)候強(qiáng)制切到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;
};

// 設(shè)置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;
        // 停頓后執(zhí)行exec獲取消息message
        setTimeout(pollingTimerFunc, 1);
    }
};

// 處理從Native返回的一條消息
// 
// 回傳消息的完整格式:
// (1)消息的長(zhǎng)度+空格+J+JavaScript代碼
// 44 Jcordova.callbackFromNative('InAppBrowser1478332075',true,1,[{"type":"loadstop","url":"http:\/\/www.baidu.com\/"}],true});
// (2)消息的長(zhǎng)度+空格+成功失敗標(biāo)示(J/S/F)+keepCallback標(biāo)示+具體的狀態(tài)碼+空格+回調(diào)ID+空格+回傳數(shù)據(jù)
// 78 S11 InAppBrowser970748887 {"type":"loadstop","url":"http:\/\/www.baidu.com\/"}
// 28 S01 Notification970748888 n0
//
// 默認(rèn)是關(guān)閉了返回Js代碼,使用返回?cái)?shù)據(jù)。
// NativeToJsMessageQueue.FORCE_ENCODE_USING_EVAL=false
function processMessage(message) {
    try {
        var firstChar = message.charAt(0);
        if (firstChar == 'J') {
            // 執(zhí)行回傳的JavaScript代碼
            eval(message.slice(1));
        } else if (firstChar == 'S' || firstChar == 'F') {
            // S代表處理成功(包含沒(méi)有數(shù)據(jù)),F(xiàn)代表處理失敗
            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') {
                // 數(shù)值:n+具體值
                payload = +message.slice(nextSpaceIdx + 2);
            } else if (payloadKind == 'A') {
                // ArrayBuffer:A+數(shù)據(jù)
                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') {
                // 二進(jìn)制字符串:S+字符串
                payload = window.atob(message.slice(nextSpaceIdx + 2));
            } else {
                // JSON:JSON串
                payload = JSON.parse(message.slice(nextSpaceIdx + 1));
            }
            // 調(diào)用回調(diào)函數(shù)
            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) {
    // 如果消息存在的話(異步?jīng)]有直接返回)
    if (messages) {

        // 把傳入的消息放到數(shù)組中
        messagesFromNative.push(messages);

        // messagesFromNative是全局的,而processMessages方法可重入,
        // 所以只需放到數(shù)組,后邊循環(huán)處理即可
        if (messagesFromNative.length > 1) {
            return;
        }

        // 遍歷從Native獲得所有消息
        while (messagesFromNative.length) {
            // 處理完成后才可以從數(shù)組中刪除
            messages = messagesFromNative[0];

            // Native返回星號(hào)代表消息需要等一會(huì)兒再取
            if (messages == '*') {
                // 刪除數(shù)組的第一個(gè)元素
                messagesFromNative.shift();
                // 再次去獲取消息
                window.setTimeout(pollOnce, 0);
                return;
            }

            // 獲取消息的長(zhǎng)度
            var spaceIdx = messages.indexOf(' ');
            var msgLen = +messages.slice(0, spaceIdx);
            // 獲取第一個(gè)消息
            var message = messages.substr(spaceIdx + 1, msgLen);
            // 截取掉第一個(gè)消息
            messages = messages.slice(spaceIdx + msgLen + 1);

            // 處理第一個(gè)消息
            processMessage(message);

            // 如果消息包含多個(gè),繼續(xù)處理;單個(gè)的話刪除本地消息數(shù)組中的數(shù)據(jù)
            if (messages) {
                messagesFromNative[0] = messages;
            } else {
                messagesFromNative.shift();
            }
        }
    }
};

module.exports = androidExec;

});