PhoneGap提供了Native Api的支持(如:重力感應、相機、聯系人、文件、地址位置…),
比如要用js獲取本機的聯系人,可以用:
var options = new ContactFindOptions();
options.filter = "李";
options.multiple = true;
var fields = ["displayName", "phoneNumbers", "emails"];
navigator.contacts.find(fields, onSuccess, onError, options);
就樣就可以獲取到名稱中包含‘李’的人了。
現在PhoneGap似乎已經成為apache開源項目
PhoneGap主頁
apache cordova(android版)
http://incubator.apache.org/cordova/index.html
源碼:https://github.com/apache/incubator-cordova-android
文檔:https://github.com/apache/incubator-cordova-docs
org.apache.cordova.CordovaChromeClient.onJsConfirm(WebView view, String url, String message, final JsResult result);
這個方法會攔截html頁面發送過來的Native Api請求(調用window.prompt()),然后交由對應的Plugin處理。
1. 服務器流程:
1) 用Plugin來提供服務供客戶端js調用,參見PhoneGap的plugin配置文件:plugins.xml,如果要自己定制plugin,需要繼承Plugin類並在plugins.xml中進行相應的配置。
2) 同步/異步
服務器根據2個參數來判斷是同步OR異步,
客戶端傳過來的異步參數 + 服務端Plugin.isSynch(action)
如果是同步,則直接把處理請求並把響應寫到客戶端
如果是異步,則啟動一個線程來處理,處理完后,將結果通過CallbackServer寫到客戶端。
3) CallbackServer相當於xmlHttpResponse,負責將數據異步寫到客戶端。它在內部會有一個socket監聽,不停的接收來自於客戶端的請求,
如果發現變量(javascript)中有數據的話,就寫到客戶端,
如果沒有,則睡眠10s,10s后,如果有數據,則寫到客戶端,否則寫一個404異常到客戶端然后此次連接中斷,重新接收新的客戶端請求(客戶端有一個輪詢,如果服務端返回404,則客戶端會每隔一段時間請求一次服務器)
4) 服務器異步返回給客戶端的數據格式:
// 正常處理后的返回(Contacts2表示請求的ID,客戶端根據這個ID調用對應的回調函數):
// 其中,紅色的json對象是PluginResult對象(自定義plugin時也需要返回一個PluginResult對象)
// 黑色的javascript腳本是經過PluginManager.exec()包裝過的,被客戶端eval解釋執行。
HTTP/1.1 200 OK
require('cordova').callbackSuccess('Contacts2',{status:1,message:[{"displayName":"%E6%9D%8E%E6%8C%9A","id":"44","rawId":"46","phoneNumbers":[{"type":"mobile","value":"18608020312","id":"92","pref":false}]}],keepCallback:false});
// 404保持連接的返回
HTTP/1.1 404 NO DATA
2. 客戶端流程:
1) 調用Native Api
PhoneGap的js框架,在調用Native Api時,都會匯聚到exec這個方法:
define('cordova/exec', function(require, exports, module) {
var cordova = require('cordova');
module.exports = function(success, fail, service, action, args) {
try {
var callbackId = service + cordova.callbackId++;
if (success || fail) {
cordova.callbacks[callbackId] = {
success: success,
fail: fail
};
}
//這里給服務器發送請求,
//service表示采用哪個
//true表示采用異步調用
//服務器會判斷這個service+action是否支持異步調用
//如果是同步,則服務器會立即返回處理結果到變量r
//如果是異步,則服務器返回空串””
var r = prompt(JSON.stringify(args),
"gap:" + JSON.stringify([service, action, callbackId, true]));
// If a result was returned
if (r.length > 0) {
……
}
} catch(e2) {
console.log("Error: " + e2);
}
};
});
2) 異步回調
define('cordova/plugin/android/callback', function(require, exports, module) {
……
callback = function() {
……
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4) {
// 服務器端返回結果數據,客戶端通過eval執行結果
// 再次往服務器發起請求。
if (xmlhttp.status === 200) {
// Need to url decode the response
var msg = decodeURIComponent(xmlhttp.responseText);
setTimeout(function() {
try {
var t = eval(msg);
} catch(e) {
console.log("JSCallback: Message from Server: " + msg);
console.log("JSCallback Error: " + e);
}
}, 1);
setTimeout(callback, 1);
}
// 服務器與客戶端約定如果404,則客戶端接着請求服務器,10s內,
// 如果客戶端調用了Native Api,則服務器服務數據后,進入上面200的邏輯,
// 如果客戶端沒調用,則服務器依然返回404,如此循環。
else if (xmlhttp.status === 404) {
setTimeout(callback, 10);
}
……
}
};
xmlhttp.open("GET", "http://127.0.0.1:" + port + "/" + token, true);
xmlhttp.send();
};
});
3. 自定義plugin流程:
1) 服務端:
繼承com.phonegap.api.Plugin類,重寫execute方法
public PluginResult execute(String action, JSONArray args, String callbackId){}
在plugins.xml中配置我們的類,
<plugin name="LoginPlugin" value="com.synnex.plugin.LoginPlugin"/>
NOTE:
isSynch()方法是告訴PhoneGap框架,此處理是同步OR異步,true表示同步,false表示異步,默認為flase
在同步處理的時候,不要去做UI操作(如:修改EditText內容),可以交由Handler更新UI。
2) 客戶端:
// 向PhoneGap注冊服務
var LoginPlugin = function(){};
LoginPlugin.prototype.dologin = function(successCallback, failureCallback, args)
{
//”LoginPlugin”需要與服務端xml中的名稱一致。
//”login”即傳給服務端excute()方法的action參數
//args必須是一個數組,對應execute()方法的args參數
return cordova.exec(successCallback, failureCallback, "LoginPlugin", "login", args);
};
cordova.addConstructor(function()
{
cordova.addPlugin("loginPlugin", new LoginPlugin());
});
// 調用時:
// 此處loginPlugin為上面addPlugin的第1個參數,
// dologin為上面LoginPlugin函數的方法名
// msg為服務端返回的數據。
plugins.loginPlugin.dologin(function(msg)
{
navigator.notification.alert(msg, undefined, "Success", "OK");
}, function(msg)
{
navigator.notification.alert(msg || "Error", undefined, "Failure", "OK");
}, [{username: "troyz", password: "123456"}]);