這裏採用的是較早版本的phonegap框架中exec模塊的實現,代碼比較直觀易懂,後來的版本爲了提升代碼的健壯性,增加了很多選擇性代碼。爲了測試方便,直接使用簡單版本的exec模塊。更高版本的phonegap庫也是在這個基礎之上擴展的,可對比學習。
define("myphonegap/exec", function(require, exports, module) {
/**
* 執行cordova命令
* 同步:返回一個JSON字符串;異步:返回空字符串"",這個時候可以根據處理結果調用回調函數
* 參數:(1)success:命令執行成功回調函數
* (2)fail:命令執行失敗回調函數
* (3)service:使用的服務名稱
* (4)action:在cordova中運行的命令
* (5)args:0或多個參數組成的數組
*/
var myphonegap= require('myphonegap');
module.exports = function(success, fail, service, action, args) {
try {
var callbackId = service + cordova.callbackId++;//內部回調id
if (success || fail) {//至少傳入了其中一個
myphonegap.callbacks[callbackId] = {success:success, fail:fail};
}
//將參數轉化爲JSON字符串去執行
var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, action, callbackId, true]));
if (r.length > 0) {
var v = JSON.parse(r);//將返回結果解析成對象
if (v.status === myphonegap.callbackStatus.OK) {
if (success) {//調用成功回調函數
try {
success(v.message);
} catch (e) {
console.log("Error in success callback: " + callbackId + " = " + e);
}
// 清除回調函數
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
return v.message;
}
else if (v.status === myphonegap.callbackStatus.NO_RESULT) {
// 清除回調函數
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
else {// 錯誤
console.log("Error: Status="+v.status+" Message="+v.message);
// 調用失敗回調函數
if (fail) {
try {
fail(v.message);
}
catch (e1) {
console.log("Error in error callback: "+callbackId+" = "+e1);
}
//清除回調函數
if (!v.keepCallback) {
delete myphonegap.callbacks[callbackId];
}
}
return null;
}
}
} catch (e2) {
console.log("Error: "+e2);
}
};
});
這裏最重要的一個函數是window提供的prompt這個函數,它實際上就是用來與android的交互的。當js調用prompt之後,本地代碼WebChromeClient實例會回調一個onJsPrompt函數,在這個回調函數中接受js發送過來的參數信息,調用相應的service和action,同步的話直接返回結果,用result.confirm提交。異步調用,在調用結果返回之後,以回調ID和返回數據爲參數,調用myphonegap.callbackSuccess或myphonegap.callbackError。
下面測試測試本地代碼和JS代碼的交互,由本地代碼調用exec模塊,給本地代碼發送消息,本地代碼解析消息調用對應的方法,返回結果後。
Js端添加測試代碼,先請求exec模塊。第一個請求執行的本地方法,類名爲Math,方法名爲plus,參數爲15,成功後打印輸出結果。第二個請求執行的本地方法,類名爲Math,方法名爲multiply,參數爲15。
//測試exec模塊
var exec = require("myphonegap/exec");
exec(function(ret){console.info("plus exec success!");console.info("15+15=" + ret)},
function(){console.info("multiply exec failed!")}, 'Math', 'plus',15);
exec(function(ret){console.info("multiply exec success!");console.info("15*15=" + ret)},
function(){console.info("multiply exec failed!")}, 'Math', 'multiply',15);
Js執行prompt方法後,android在WebChromeClient的onJsPrompt被調用,爲了測試簡單這裏只用了action區分調用的本地方法沒有使用service區分調用哪個類,plus操作爲同步方法,直接返回,multiply模擬爲異步方法,通過回調js裏面的方法返回結果。
private class MyWebChromeClient extends WebChromeClient{
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
try {
//array的解析與js端的封裝有關,字串過濾了”gap:”4個字符
JSONArray array = new JSONArray(defaultValue.substring(4));
String service = array.getString(0);
String action = array.getString(1);
String callbackId = array.getString(2);
if(action.equals("plus")){
int a = Integer.parseInt(message);
int ret = plus(a,a);
String str = "{\"status\":1,\"message\":" ;
str += ret;
str += "}";
result.confirm(str);
}else if(action.equals("multiply")){
int a = Integer.parseInt(message);
int ret = multiply(a,a);
String str = "{\"status\":1,\"message\":" ;
str += ret;
str += "}";
result.confirm("");
//此處採用異步模式,不使用result.confirm直接返回結果了。
mWebView.loadUrl("javascript:myphonegap.callbackSuccess(\""+callbackId+"\","+str+")");
}
} catch (JSONException e) {
e.printStackTrace();
}
return true;
}
本次測試採用了android真機測試。測試結果: