精通Cocos2d-x游戏开发(进阶卷)
上QQ阅读APP看书,第一时间看更新

3.4 AssetsManagerEx内部实现流程简析

1.初始化本地Manifest

AssetsManagerEx进行热更新的第一步是初始化Manifest,除了构造函数传入的本地Manifest文件,还存在其他3个Manifest文件,分别是CacheManifest、CacheVersion和TempManifest,这3个文件都是从服务端下载的。

LocalManifest是游戏安装包里的Manifest文件,CacheManifest和CacheVersion是从服务器下载的Manifest文件,TempManifest是本次增量更新生成的临时文件,内容和CacheManifest一样,主要用于辅助处理更新到一半被异常中断后的恢复处理。

首先初始化LocalManifest对象和CacheManifest对象,初始化Manifest对象的规则是先检查指定Manifest文件路径,如果存在该文件则读取并初始化,如果初始化失败(例如文件只下载了一半)则删除该文件(因为这是一个坏的文件)并释放Manifest对象。如果两个对象都初始化成功了,则会比较两者的版本号,如果LocalManifest的版本号大于CacheManifest的版本号,则将我们设置的增量更新存储目录删除并使用LocalManifest对象,否则使用CacheManifest对象。如果LocalManifest对象创建失败,那么AssetsManagerEx会发送ERROR_NO_LOCAL_MANIFEST错误。LocalManifest对象创建成功之后,AssetsManagerEx会获取其资源列表并将配置的搜索路径添加到引擎的搜索路径中,并设置其优先级。完成了LocalManifest的初始化之后,初始化TempManifest文件。

第一次运行程序是没有CacheManifest文件的,需要从服务器下载,下载之后就会一直使用CacheManifest文件作为本地版本来与服务器的Manifest文件进行版本判断。当有新的更新时,CacheManifest文件也会被替换成最新的Manifest文件,因为安装包中的Manifest文件是无法修改的,所以如果使用安装包中的Manifest文件来与服务器的Manifest文件进行判断,那么每次的结果都是需要更新。

当LocalManifest的版本号大于CacheManifest的版本号时,我们设置的增量更新存储目录会被删除,这里只存在一种可能性,就是我们更新了新的完整包,不是增量更新,而是大版本的完全更新。这种情况下安装包中的内容要比增量更新存储目录中的内容更新,因为更新程序不会删除程序可写目录下的文件的,而增量更新存储目录的优先级要高于安装包内的游戏目录,如果不删除增量更新存储目录的话,就会加载一些旧版本的资源,这就是为什么要删除存储目录的原因。

2.检查更新

调用AssetsManagerEx的update()方法,首先会下载VersionManifest文件,downloadVersion()方法会判断是否配置了VersionManifest文件的下载路径,如果是则将其下载到CacheVersion文件路径下,如没有配置或者下载失败则会直接下载服务器的ProjectManifest文件,将其下载到TempManifest文件路径下,如果下载失败或者没有配置ProjectManifest文件的下载路径,AssetsManagerEx会发送ERROR_DOWNLOAD_MANIFEST错误,我们可以再次调用AssetsManagerEx的update()方法进行重试。

成功下载VersionManifest或ProjectManifest文件之后都会执行版本判断,检查是否需要更新,检查会判断本地Manifest文件和远程Manifest文件的版本字段是否相同,不同则触发更新,相同则进一步判断版本组中的每一个小版本,也就是版本组字段中的内容,如果远程Manifest文件中有新的或不同的小版本,则触发更新。

3.开始更新

开始更新时会先判断TempManifest对象是否存在,以及TempManifest对象的版本是否与远程Manifest的版本相同,是则说明该版本的上次更新没有完成,所以只需要继续下载之前没有下载完的部分即可,因为每下载完成一个资源,就会修改TempManifest文件中对应资源的状态字段,所以只需要下载TempManifest对象中状态为未下载的资源那部分资源即可。

否则会根据本地Manifest文件和远程Manifest文件计算出资源差异列表,这个差异列表包含了要更新以及删除的资源,当一个资源在本地Manifest文件中而不在远程Manifest文件中,则会将这个资源标记为待删除资源,反之则会将这个资源添加到要下载的资源列表中,同时存在的资源但MD5字段不同,也会被添加到要下载的资源列表中。计算出要下载的资源列表之后,AssetsManagerEx就会调用Downloader对象进行下载。

4.更新结果

当出现下载失败的资源时,AssetsManagerEx都会将下载失败的资源放到一个失败列表中,而每成功下载一个资源,AssetsManagerEx都会更新TempManifest对象的资源列表中指定资源的状态为已完成,并判断该资源是否在失败列表中,是则从失败列表中移除,减少待下载资源的计数器,最后判断待下载资源的计数器是否小于等于0,是则进一步判断失败列表是否为空,为空则执行下载成功的逻辑,否则保存TempManifest对象,解压已下载的zip压缩包(这里是阻塞的),并发送更新失败的消息。

实际使用中如果在更新的过程中出现资源下载失败的情况,是不会执行到更新失败这条分支的,因为待下载资源的计数器会一直大于0。另外,如果在更新的过程中强制退出游戏,下次进入时已经下载好的文件会重新从头开始下载,只有正在下载的文件会断点续传。

更新成功之后,AssetsManagerEx会用TempManifest文件来替换CacheManifest文件,并设置新的搜索路径,接下来开启一条线程逐个解压压缩文件,并将zip文件删除,所有文件解压完成后会发送更新成功的消息。