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