首先, 來看一下phonegap 初始化流程以及Native 與 JS 交互流程圖。

說明:socket server模式下, phonegap.js 源碼實現的采用1 毫秒執行一次XHR請求, 當Native JS 隊列里面有JS語句數據時,才是真正的1毫秒調用一下; 當沒有數據, scoket server 會阻塞10毫秒, 也就是XHR 要等10秒鍾才能收到結果,並進行下一次的輪詢。
1、Activity繼承 DroidGap (extends PhonegapActivity)
從phonegap.xml 中加載白名單配置 和 log配置
2、loadUrl (每個Activity 都初始化一次)
》》初始化webview
》》初始化callbackServer
》》插件管理器PluginManager
3、加載插件配置:
》》讀取 plugins.xml 配置,用map存儲起來。
<plugins> <plugin name="Camera" value="com.phonegap.CameraLauncher"/> <plugin name="Contacts" value="com.phonegap.ContactManager"/> <plugin name="Crypto" value="com.phonegap.CryptoHandler"/> <plugin name="File" value="com.phonegap.FileUtils"/> <plugin name="Network Status" value="com.phonegap.NetworkManager"/> </plugins>
說明:
name 是別名,javascript調用時通過別名來調用。
value:java具體實現類
web頁面調用(例如查找聯想人)
PhoneGap.exec(successCB, errorCB, "Contacts", "search", [fields, options]);
4、插件實現
》》編程java類,繼承Plugin類(Plugin 實現了IPlugin接口),並實現execute方法。
例如聯系人管理插件:
public class ContactManager extends Plugin{
/**
* action : 用來指定一個具體動作 search 表示搜索聯系人
* args: 方法參數
* callbackId:js與java指定一個標識,
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
try {
if (action.equals("search")) {
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
return new PluginResult(status, res, "navigator.contacts.cast");
}
else if (action.equals("save")) {
String id = contactAccessor.save(args.getJSONObject(0));
if (id != null) {
JSONObject res = contactAccessor.getContactById(id);
if (res != null) {
return new PluginResult(status, res);
}
}
}
else if (action.equals("remove")) {
if (contactAccessor.remove(args.getString(0))) {
return new PluginResult(status, result);
}
}
// If we get to this point an error has occurred
JSONObject r = new JSONObject();
r.put("code", UNKNOWN_ERROR);
return new PluginResult(PluginResult.Status.ERROR, r);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
}
5、polling和server初始化
android DroidGap 初始化時,如果loadUrl的url不是以file:// 開頭時,polling = true, 否則是socket server方式
代碼見CallbackServer.java 類init方法
public void init(String url) {
//System.out.println("CallbackServer.start("+url+")");
// Determine if XHR or polling is to be used
if ((url != null) && !url.startsWith("file://")) {
this.usePolling = true;
this.stopServer();
}
else if (android.net.Proxy.getDefaultHost() != null) {
this.usePolling = true;
this.stopServer();
}
else {
this.usePolling = false;
this.startServer();
}
}
6、phonegap.js 關鍵代碼說明
phonegap.js在啟動時,首先會通過prompt("usePolling", "gap_callbackServer:")獲取調用方式: XHR 輪詢 OR prompt 輪詢, 如果是XHR的話, 會啟動XHR調用獲取http server端口 和token。
方法PhoneGap.Channel.join 啟動 js server 或者polling調用
UsePolling 默認為false。 通過var polling = prompt("usePolling", "gap_callbackServer:") 獲取調用方式。
PhoneGap.Channel.join(function () {
// Start listening for XHR callbacks
setTimeout(function () {
if (PhoneGap.UsePolling) {
PhoneGap.JSCallbackPolling();
}
else {
console.log('PhoneGap.Channel.join>>>>>>>>>>>>>>>>>>>>>>>>>');
//phonegap js 首次啟動獲取js調用Native方式
var polling = prompt("usePolling", "gap_callbackServer:");
PhoneGap.UsePolling = polling;
if (polling == "true") {
PhoneGap.UsePolling = true;
PhoneGap.JSCallbackPolling();
}
else {
PhoneGap.UsePolling = false;
PhoneGap.JSCallback();
}
}
}, 1);
}
XHR輪詢:PhoneGap.JSCallback方法
通過XHR 與java端 socket進行通信,每一毫秒執行一次JSCallback,從android socket獲取javascript執行結果代碼,最后通過eval動態執行javascript
XHR調用, 通過prompt 獲取socket端口 和 token(uuid)
if (PhoneGap.JSCallbackPort === null) {
PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:");
console.log('PhoneGap.JSCallback getPort>>>>>>>>>>>>>>>>>>>>>>>>>:' + PhoneGap.JSCallbackPort);
}
if (PhoneGap.JSCallbackToken === null) {
PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:");
console.log('PhoneGap.JSCallback getToken>>>>>>>>>>>>>>>>>>>>>>>>>:' + PhoneGap.JSCallbackToken);
}
xmlhttp.open("GET", "http://127.0.0.1:" + PhoneGap.JSCallbackPort + "/" + PhoneGap.JSCallbackToken, true);
xmlhttp.send();
XHR返回結果代碼片段
var msg = decodeURIComponent(xmlhttp.responseText);
setTimeout(function () {
try {
var t = eval(msg);
}
catch (e) {
// If we're getting an error here, seeing the message will help in debugging
console.log("JSCallback: Message from Server: " + msg);
console.log("JSCallback Error: " + e);
}
}, 1);
setTimeout(PhoneGap.JSCallback, 1);
}
prompt輪詢: PhoneGap.JSCallbackPolling方法
PhoneGap.JSCallbackPolling = function () { // Exit if shutting down app if (PhoneGap.shuttingDown) { return; } // If polling flag was changed, stop using polling from now on if (!PhoneGap.UsePolling) { PhoneGap.JSCallback(); return; } var msg = prompt("", "gap_poll:"); if (msg) { setTimeout(function () { try { var t = eval("" + msg); } catch (e) { console.log("JSCallbackPolling: Message from Server: " + msg); console.log("JSCallbackPolling Error: " + e); } }, 1); setTimeout(PhoneGap.JSCallbackPolling, 1); } else { setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod); } };
7、總結
1、phonegap android 插件管理器PluginManager初始化時, 是每個Activity都要初始化一次, 數據都緩存一次, 導致同一份數據緩存多次。-- 暫不清楚為啥這樣實現? 難道是phonegap 框架是為單webview 實現的,如果有知道原因的請告知一下。
2、同第1點一樣, Socket Server 每個Activity都會初始化一下, 如果loadUrl 的url類型不同,會不會導致scoket server狀體錯亂, 待驗證!
3、phonegap 采用 prompt 和 XHR 輪詢機制,一是會導致手機耗電情況嚴重, 二是了解到prompt 調用是會阻塞js執行的, 這樣導致影響到頁面加載速度。
phonegap 已經改名cordova, 在最新版本cordova 框架里面已經去掉了socket server模式, 詳細請查看:http://www.cnblogs.com/hubcarl/p/4202784.html
