phonegap 框架詳解

首先, 來看一下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存儲起來。

1
2
3
4
5
6
7
<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方法。
例如聯繫人管理插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:") 獲取調用方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PhoneGap.Channel.join(function () {
 
    // Start listening for XHR callbacks
    setTimeout(function () {
      if (PhoneGap.UsePolling) {
        PhoneGap.JSCallbackPolling();
      }
      else {
        console.log('PhoneGap.Channel.join>>>>>>>>>>>>>>>>>>>>>>>>>');<br>       <span style="color: #ff6600;"//phonegap js 首次啓動獲取js調用Native方式</span>
        var polling = prompt("usePolling""gap_callbackServer:");
        PhoneGap.UsePolling = polling;
        if (polling == "true") {
          PhoneGap.UsePolling = true;
          <span style="color: #ff6600;">PhoneGap.JSCallbackPolling();</span>
        }
        else {
          PhoneGap.UsePolling = false;
         <span style="color: #ff6600;"> PhoneGap.JSCallback();</span>
        }
      }
    }, 1);
}

  

XHR輪詢:PhoneGap.JSCallback方法

通過XHR 與java端 socket進行通信,每一毫秒執行一次JSCallback,從android socket獲取javascript執行結果代碼,最後通過eval動態執行javascript

XHR調用, 通過prompt 獲取socket端口 和 token(uuid)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if (PhoneGap.JSCallbackPort === null) {
   PhoneGap.JSCallbackPort = <span style="color: #ff6600;">prompt("getPort""gap_callbackServer:");</span>
   console.log('PhoneGap.JSCallback getPort>>>>>>>>>>>>>>>>>>>>>>>>>:' + PhoneGap.JSCallbackPort);
}
if (PhoneGap.JSCallbackToken === null) {
  PhoneGap.JSCallbackToken =<span style="color: #ff6600;"> prompt("getToken""gap_callbackServer:");</span>
  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);
 <span style="color: #ff6600;">setTimeout(PhoneGap.JSCallback, 1);</span><br>}

  

prompt輪詢: PhoneGap.JSCallbackPolling方法

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<span style="color: #ff6600;">PhoneGap.JSCallbackPolling</span> = 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);
      <span style="color: #ff6600;">setTimeout(PhoneGap.JSCallbackPolling, 1);</span>
    }
    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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章