Cocos2d-x Lua中的AssetsManagerEx

Cocos2d-x Lua中的热更机制主要是通过AssetsManagerEx来实现的。

传统的热更方式

传统PC的方式是将差异文件打包成一个压缩包,客户端根据大版本差异,将对应的压缩包下载到本地后解压覆盖。这种做法的好处在于下载一个文件会比较快。缺点在于当客户端版本比较多的时候,升级压缩包会变得很多,进而难以维护。其次是如果压缩包体积过大,解压时间会比较长,而且不容易制作进度条,导致程序感觉被卡住了。

AssetsManagerEx采用比较所有文件是否一致的方式,首先需要将所有文件的md5散列表计算出来,然后生成对应格式的manifest文件。这种方式更类似网页加载的方式,即网页显示时若本地有缓存则比对缓存与服务器是否一致,若相同则使用本地缓存,若不一直则下载更新。这样服务器只需要保存一个完整的最新客户端。如果更新了某个文件,只需要替换服务器上对应的文件,然后修改文件中的版本号和文件的md5散列码。此种做法的优点在于没有解压缩的过程,缺点是更新文件比较多的时候会略慢,而且有一定几率的下载失败。

热更新基本原理

  • 不能更新主程序,只能更新资源和脚本文件
    C++生成的主程序只能通过升级安全包,其他文件可通过HTTP动态下载到手机中,然后程序内部重新执行入口函数,以达到更新代码逻辑和资源图片的效果,所以C++代码在程序上线前必须保证最完善。
  • 下载的文件会存放到手机的可写入目录中,该目录中文件的优先级必须高于程序原始安装目录。
    以HTTP方式下载后的文件并不能直接覆盖安装程序所在目录中的同名文件,因为权限不足。由于Lua使用动态加载,只要在搜索路径中将可写入目录的优先级设置为最高,那么即使两个目录中存在同名文件,程序也会优先使用最新下载的文件。AssetsManagerEx类的create()方法中会见用户定义的可写入目录设置为最高优先级。
  • 资源文件和脚本文件被加载后,即使程序在运行中文件也可以直接被删除,以确保文件可以被动态替换。
  • 手机本地和服务器中保存了程序中所有文件的md5散列码列表,通过比对两个文件中md5散列码中,过滤出需要更新的文件,并进行下载。

热更新的实现流程

  1. 开发可以遍历项目中所有文件并生成对应版本的version.manifestproject.manifest文件的工具

version.manifestproject.manifest的格式本质上是一致的,version.manifest中只包含大版本号信息,而project.manifest包含version.manifest中所有内容和所有项目文件信息,这样做的好处在于当项目文件很多的时候,project.manifest会比较大,所以单独分割出来一个version.manifest来比较大版本,如果大版本不一致就不用下载project.manifest

对于项目src目录下的cocosframework等库文件,如果确定不会修改就不用生成到project.manifest文件中,如果确实需要修改某个文件,可以手动加入到project.manifest文件中。建议不要修改,避免日后维护困难。

/*version.manifest*/
{
  //服务器中存放完整最新版本程序的目录位置,即项目的根目录。
  "packageUrl":"http://192.168.0.164/update/files/",
  //服务器存放version.manifest文件的URL地址
  "remoteVersionUrl":"http://192.168.0.164/update/version/version.manifest",
  //服务器存放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*/
{
  //服务器中存放完整最新版本程序的目录位置,即项目的根目录。
  "packageUrl":"http://192.168.0.164/update/files/",
  //服务器存放version.manifest文件的URL地址
  "remoteVersionUrl":"http://192.168.0.164/update/version/version.manifest",
  //服务器存放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表示,更新完毕后进行解压。
    //由于zip文件是独立打包计算md5,日后的比较必须以zip包做比较,实际维护起来比较麻烦,不建议使用。
    "res/zip/test.zip":{
      "md5":"e6aed0272011da3039ccc1008040cbce",
      "compressed":true
    }
  }
}
  1. project.manifest文件存放到主程序某个目录(version)中并制作安装包。
srv/version/project.manifest
  1. version.manifest存放到remoteVersionUrl对应服务器指定目录下
  2. project.manifest存放到remoteManifestUrl对应服务器指定目录下
  3. 将最新的代码存放到packageUrl对应服务器指定目录下,注意要与assets下的路径对应。
  4. 客户端执行更新代码
-- 设置新文件保存路径
local writablePath = cc.FileUtils:getInstance():getWritablePath()
local storagePath = writablePath.."version"
-- 创建资源管理器
local file = "src/version/project.manifest"
local assetsManagerEx = cc.AssetsManagerEx:create(file, storagePath)
assetsManagerEx:retain()
-- 监听下载消息
local function assetsManagerExHandler(event)
    local eventCode = event:getEventCode()
    local assetManagerExCode = cc.EventAssetsManagerEx.EventCode
    if eventCode == assetManagerExCode.ALREADY_UP_TO_DATE then
        print("当前已是最新版本")
        -- 进入游戏主界面
    end
    if eventCode == assetManagerExCode.NEW_VERSION_FOUND then
        print("发现新版本,开始升级...")
    end
    if eventCode == assetManagerExCode.PROGRESSION then
        print("当前更新进度为"..event.getPercent())
    end
    if eventCode == assetManagerExCode.UPDATE_FINISHED then
        print("更新完毕,准备重启...")
        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路径一旦确认不能更换否则会造成多版本混乱
  • assetsManagerEx:retain()缺失会造成下载失败
  • 如果文件是只读的,会造成更新失败。
  • 客户端获取当前本地版本
local localManifest = assetsManagerEx:getLocalManifest()
local version = localManifest:getVersion()

你可能感兴趣的:(Cocos2d-x Lua中的AssetsManagerEx)