Cocos2d-x Lua中的熱更機(jī)制主要是通過AssetsManagerEx
來實(shí)現(xiàn)的亿驾。
傳統(tǒng)的熱更方式
傳統(tǒng)PC的方式是將差異文件打包成一個(gè)壓縮包亭敢,客戶端根據(jù)大版本差異该园,將對應(yīng)的壓縮包下載到本地后解壓覆蓋槽驶。這種做法的好處在于下載一個(gè)文件會(huì)比較快审洞。缺點(diǎn)在于當(dāng)客戶端版本比較多的時(shí)候呼畸,升級壓縮包會(huì)變得很多痕支,進(jìn)而難以維護(hù)。其次是如果壓縮包體積過大蛮原,解壓時(shí)間會(huì)比較長卧须,而且不容易制作進(jìn)度條,導(dǎo)致程序感覺被卡住了儒陨。
AssetsManagerEx
采用比較所有文件是否一致的方式花嘶,首先需要將所有文件的md5散列表計(jì)算出來,然后生成對應(yīng)格式的manifest
文件蹦漠。這種方式更類似網(wǎng)頁加載的方式椭员,即網(wǎng)頁顯示時(shí)若本地有緩存則比對緩存與服務(wù)器是否一致,若相同則使用本地緩存笛园,若不一直則下載更新隘击。這樣服務(wù)器只需要保存一個(gè)完整的最新客戶端。如果更新了某個(gè)文件研铆,只需要替換服務(wù)器上對應(yīng)的文件埋同,然后修改文件中的版本號和文件的md5散列碼。此種做法的優(yōu)點(diǎn)在于沒有解壓縮的過程蚜印,缺點(diǎn)是更新文件比較多的時(shí)候會(huì)略慢莺禁,而且有一定幾率的下載失敗。
熱更新基本原理
- 不能更新主程序窄赋,只能更新資源和腳本文件
C++生成的主程序只能通過升級安全包哟冬,其他文件可通過HTTP動(dòng)態(tài)下載到手機(jī)中楼熄,然后程序內(nèi)部重新執(zhí)行入口函數(shù),以達(dá)到更新代碼邏輯和資源圖片的效果浩峡,所以C++代碼在程序上線前必須保證最完善可岂。 - 下載的文件會(huì)存放到手機(jī)的可寫入目錄中,該目錄中文件的優(yōu)先級必須高于程序原始安裝目錄翰灾。
以HTTP方式下載后的文件并不能直接覆蓋安裝程序所在目錄中的同名文件缕粹,因?yàn)闄?quán)限不足。由于Lua使用動(dòng)態(tài)加載纸淮,只要在搜索路徑中將可寫入目錄的優(yōu)先級設(shè)置為最高平斩,那么即使兩個(gè)目錄中存在同名文件,程序也會(huì)優(yōu)先使用最新下載的文件咽块。AssetsManagerEx
類的create()
方法中會(huì)見用戶定義的可寫入目錄設(shè)置為最高優(yōu)先級绘面。 - 資源文件和腳本文件被加載后,即使程序在運(yùn)行中文件也可以直接被刪除侈沪,以確保文件可以被動(dòng)態(tài)替換揭璃。
- 手機(jī)本地和服務(wù)器中保存了程序中所有文件的md5散列碼列表,通過比對兩個(gè)文件中md5散列碼中亭罪,過濾出需要更新的文件瘦馍,并進(jìn)行下載。
熱更新的實(shí)現(xiàn)流程
- 開發(fā)可以遍歷項(xiàng)目中所有文件并生成對應(yīng)版本的
version.manifest
和project.manifest
文件的工具
version.manifest
和project.manifest
的格式本質(zhì)上是一致的应役,version.manifest
中只包含大版本號信息情组,而project.manifest
包含version.manifest
中所有內(nèi)容和所有項(xiàng)目文件信息,這樣做的好處在于當(dāng)項(xiàng)目文件很多的時(shí)候扛吞,project.manifest
會(huì)比較大呻惕,所以單獨(dú)分割出來一個(gè)version.manifest
來比較大版本,如果大版本不一致就不用下載project.manifest
滥比。
對于項(xiàng)目src
目錄下的cocos
亚脆、framework
等庫文件,如果確定不會(huì)修改就不用生成到project.manifest
文件中盲泛,如果確實(shí)需要修改某個(gè)文件濒持,可以手動(dòng)加入到project.manifest
文件中。建議不要修改寺滚,避免日后維護(hù)困難柑营。
/*version.manifest*/
{
//服務(wù)器中存放完整最新版本程序的目錄位置,即項(xiàng)目的根目錄村视。
"packageUrl":"http://192.168.0.164/update/files/",
//服務(wù)器存放version.manifest文件的URL地址
"remoteVersionUrl":"http://192.168.0.164/update/version/version.manifest",
//服務(wù)器存放project.manifest文件的URL地址
"remoteManifestUrl":"http://192.168.0.164/update/version/project.manifest",
//程序版本號官套,采用 "大版本.日期.小版本" 格式
"version":"1.20190112.01",
//客戶端引擎版本
"engineVersion":"Cocos2d-lua v3.3 Final"
}
/*project.manifest*/
{
//服務(wù)器中存放完整最新版本程序的目錄位置,即項(xiàng)目的根目錄。
"packageUrl":"http://192.168.0.164/update/files/",
//服務(wù)器存放version.manifest文件的URL地址
"remoteVersionUrl":"http://192.168.0.164/update/version/version.manifest",
//服務(wù)器存放project.manifest文件的URL地址
"remoteManifestUrl":"http://192.168.0.164/update/version/project.manifest",
//程序版本號奶赔,采用 "大版本.日期.小版本" 格式
"version":"1.20190112.01",
//客戶端引擎版本
"engineVersion":"Cocos2d-lua v3.3 Final",
//資源文件
"assets":{
"res/images/test.png":{
"md5":"e6aed0272011da3039ccc1008040cbce"
},
//資源文件為zip且compress為true表示惋嚎,更新完畢后進(jìn)行解壓。
//由于zip文件是獨(dú)立打包計(jì)算md5站刑,日后的比較必須以zip包做比較另伍,實(shí)際維護(hù)起來比較麻煩,不建議使用绞旅。
"res/zip/test.zip":{
"md5":"e6aed0272011da3039ccc1008040cbce",
"compressed":true
}
}
}
- 將
project.manifest
文件存放到主程序某個(gè)目錄(version)中并制作安裝包摆尝。
srv/version/project.manifest
- 將
version.manifest
存放到remoteVersionUrl
對應(yīng)服務(wù)器指定目錄下 - 將
project.manifest
存放到remoteManifestUrl
對應(yīng)服務(wù)器指定目錄下 - 將最新的代碼存放到
packageUrl
對應(yīng)服務(wù)器指定目錄下,注意要與assets下的路徑對應(yīng)因悲。 - 客戶端執(zhí)行更新代碼
-- 設(shè)置新文件保存路徑
local writablePath = cc.FileUtils:getInstance():getWritablePath()
local storagePath = writablePath.."version"
-- 創(chuàng)建資源管理器
local file = "src/version/project.manifest"
local assetsManagerEx = cc.AssetsManagerEx:create(file, storagePath)
assetsManagerEx:retain()
-- 監(jiān)聽下載消息
local function assetsManagerExHandler(event)
local eventCode = event:getEventCode()
local assetManagerExCode = cc.EventAssetsManagerEx.EventCode
if eventCode == assetManagerExCode.ALREADY_UP_TO_DATE then
print("當(dāng)前已是最新版本")
-- 進(jìn)入游戲主界面
end
if eventCode == assetManagerExCode.NEW_VERSION_FOUND then
print("發(fā)現(xiàn)新版本堕汞,開始升級...")
end
if eventCode == assetManagerExCode.PROGRESSION then
print("當(dāng)前更新進(jìn)度為"..event.getPercent())
end
if eventCode == assetManagerExCode.UPDATE_FINISHED then
print("更新完畢,準(zhǔn)備重啟...")
app:run()
end
if eventCode == assetManagerExCode.ERROR_NO_LOCAL_MANIFEST then
print("熱更失敾瘟铡:本地不存在manifest文件")
end
if eventCode == assetsManagerExCode.ERROR_DOWNLOAD_MANIFEST then
print("熱更失斁世省:manifest文件下載失敗")
end
if eventCode == assetsManagerExCode.ERROR_PARSE_MANIFEST then
print("熱更失敗:manifest文件解析失敗")
end
if eventCode == assetManagerExCode.ERROR_UPDATING then
print("熱更失斝痢:文件更新失敗")
end
end
local dispatcher = cc.Director:getInstance():getEventDispatcher()
local eventListenerAssetsManagerEx = cc.EventListenerAssetsManagerEx:create(assetsManagerEx, assetsManagerExHandler)
dispatcher:addEventListenerWithFixedPriority(eventListenerAssetsManagerEx, 1)
-- 檢查版本并升級
assetsManagerEx:update()
使用注意
-
storagePath
路徑一旦確認(rèn)不能更換否則會(huì)造成多版本混亂 -
assetsManagerEx:retain()
缺失會(huì)造成下載失敗 - 如果文件是只讀的,會(huì)造成更新失敗绣否。
- 客戶端獲取當(dāng)前本地版本
local localManifest = assetsManagerEx:getLocalManifest()
local version = localManifest:getVersion()