quick-cocos2d-x基於源碼加密打包功能的更新策略(2)

前一篇:quick-cocos2d-x基於源碼加密打包功能的更新策略(1) 安全

2、更新原理討論及更復雜的更新功能

1.更新原理

在前面的更新過程當中,從服務器取文件列表,並根據文件列表再更新相關的文件,這都是很好理解的。固然其中還有些流程細節關係到健壯性、續傳、文件版本分發等,咱們能夠後面再討論。 服務器

對於一些剛開始學習Quick-x的朋友來講,可能但願瞭解的是,這一更新機制的替換原理是什麼,爲何新的文件下載後可以替代原來的代碼生效呢? app

從前面咱們參考的源文件編譯及加密的相關文章,能夠清楚的看到,初始代碼打包成的game.zip文件,是在AppDelegate.ccp中,在程序啓動前經過loadChunksFromZIP載入的。這一載入工做實際上已經將全部代碼都加載了,以後調用require時,將會直接 調用而不會再去找代碼文件來載入。也正由於如此,一些朋友會感到迷惑,即便下載了新的代碼文件,又如何讓它生效呢? 框架

其實很簡單,loadChunksFromZIP是能夠屢次調用的,並且若是第二次載入的包中的代碼模塊與以前載入的模塊有重名,新的模塊會覆蓋舊的模塊。 學習

在Quick-x的Lua代碼中,對應的調用接口是CCLuaLoadChunksFromZIP。有了這個接口,咱們下載新的代碼包後,就能夠本身加載了。在update.lua裏,在下載完成後,會自動將act標記爲load的文件加載一次,這樣,模塊的新代碼就取代了舊的代碼,也就實現了咱們以前看到的更新功能。 測試

原理很簡單,彷佛沒問題了?惋惜問題沒這麼簡單。這是由於require的機制問題。若是在第二次載入包以前,某一個模塊文件已經被require過,那麼,雖然第二次載入後,這一模塊的代碼已經被更新了,但再次require時,因爲此模塊不會被真正調用而是直接取原來的調用結果,將會形成更新不生效。 ui

下面咱們經過調試一個新的更新情景來講明這個問題及解決方法。 加密

2.已經require過的模塊的更新

假設如今咱們但願更新一下界面上的圖片。 lua

若是隻是更換個別圖片,這是很簡單的,只須要在代碼裏newSprite裏在圖片文件名前加上完整的下載目錄路徑,而後將新的圖片放到服務器上,在flist文件中添加圖片文件名便可。不建議在要更新的代碼裏使用addSearchPath來添加搜索路徑,由於在圖片同名的狀況下並不能保證優先查找到的是新的文件。 spa

若是是更新紋理圖,仍然可使用上述明確指定資源路徑的方式,同時也是建議的方式。所以,對於咱們以前MainScene.lua的代碼,能夠作如下修改(假設device.writablePath是下載路徑):

display.addSpriteFramesWithFile(device.writablePath..GAME_TEXTURE_DATA_FILENAME, device.writablePath..GAME_TEXTURE_IMAGE_FILENAME)
self.bg = display.newSprite("#logo.png", display.cx-200, display.cy-200)
self:addChild(self.bg)

固然這不是惟一的改法。爲了說明主題,咱們換成修改config模塊的方式。此次不修改MainScene.lua,而是在config.lua中,修改原來的紋理圖定義以下:

GAME_TEXTURE_DATA_FILENAME = device.writablePath.."game.plist"
GAME_TEXTURE_IMAGE_FILENAME = device.writablePath.."game.png"

要注意的是,若是不是更新代碼,這樣寫是有問題的。一般config模塊都是最先被require的,甚至還在framework.init模塊以前,此時device都尚未定義,因此若是是正常流程就會出錯。但這裏是更新代碼,在被調用以前,update模塊已經調用過framework.init,因此device已經被初始化過了。

這就引出了咱們的問題,既然config模塊在早期已經被調用,那麼,當新的config下載到客戶端後被加載時,再require實際上已經不能讓它生效了!

最直接的解決辦法是,在從新require以前,調用如下的代碼:

package.loaded["config"] = nil

即將config的調用標記清除。這樣,再次require時就能讓Lua程序從新去執行新的config模塊了。咱們將這條語句加到appentry模塊的開頭,這是最合適的地方了。

咱們仍然將修改後的config.lua和appentry.lua打包成update.bin,和新的圖片資源文件一塊兒放到服務器上,此次服務器的flist文件以下:

local list = {
  ver = "1.0.3",
  stage = {
   {name="game.plist", code="29d103e9831720c1be12d8b33a1ea762", },
   {name="game.png", code="e9dd2797018cad79186e03e8c5aec8dc", },
   {name="update.bin", code="a681c6a002989832645ed26b766c7afa", act="load"},
  },
  remove = {
  },
}
return list
在客戶端啓動程序,版本自動被更新,進入MainScene顯示的圖片變成了新的,成功了!

你們能夠自行驗證不清除調用標記的狀況。要注意的是若是是在player上,顯示的圖片同樣會變成新的,這是由於device.writablePath在player上恰好是最優先的搜索路徑,因此下載後的圖片會被先搜到。所以這個測試最好下載到其餘目錄下,或者在新的config模塊里加一條調試輸出語句,這樣就很容易確認新的模塊是否真的被執行了。

由上述例子能夠看到,更新模塊時,困難的地方就在於判斷重加載前有沒有已經被調用的模塊。特別是做在應用運行過程當中動態更新的方案時,更須要對流程有清楚的把握。

即便是如今實現的這個預更新的方案,在解決了config模塊的重載調用問題後,仍然要面對一個相對更困難的問題:若是framework模塊須要更新,應該怎麼辦?

3.深層邏輯更新的處理方式:強制覆蓋式調用

framework模塊是Quick-x的核心之一,舊版本里是在main.lua裏載入,如今的版本更是將它的載入位置提早到了Lua程序入口被調用以前的C++代碼中,而一般framework的init模塊也是早早會被調用。即便是update模塊,也是先調用了init才能方便的實現更新功能。雖然這一模塊更新的機率很小,但做爲解決方案,仍是要考慮清楚應該如何對它進行更新。

首先會想到的,是象以前處理config模塊同樣,清除調用標記。然而仔細考慮,會以爲不太行得通。由於看代碼能夠知道,init模塊還調用了framework裏不少的其餘模塊,在修改以後要理清哪些模塊須要重調用是很繁瑣的,而繁瑣就容易出錯。所以這不是最好的解決方案。

通過考慮,我以爲最方便的是這樣一種解決方式:若是須要更新framework模塊,那麼,在打包時,不將代碼放在framework目錄下,而是其餘的目錄,如「framework1」。這樣,全部的模塊就變成了framework1.init、framework1.device等等,在新的代碼裏調用「require "framework1.int"」,將會真正從新運行框架代碼,而沒必要擔憂有哪一個模塊沒有從新運行生效。

這就是「強制覆蓋式調用」的含義,它在不少情形下能夠避免過於複雜的更新邏輯的判斷,推薦給你們。

再舉個例子,若是更新模塊自己須要更新,那應該怎麼辦呢?若是要爲此在更新模塊中預留更新邏輯,會很是複雜難解。然而,若是跳出來看,其實徹底能夠在修改完update模塊後,打包成一個updateNew模塊放到服務器,讓原更新模塊下載後強制調用,這樣一來,整個處理就簡單了。

經過上面的討論,如今咱們已經能確認,update模塊的更新功能是有效的。接下來咱們還要討論更新方案的另外一個要點:安全性。

若是由於更新失敗形成客戶端沒法啓動,那無疑是致命的。幸運的是,Quick-x的打包機制能讓咱們保有一個原始版本,即便是在最不幸的出錯狀況下,程序仍然能回到最初的版本而不至於崩漬。

後一篇:

quick-cocos2d-x基於源碼加密打包功能的更新策略(3)

相關文章
相關標籤/搜索