AssetsManagerEx 组件使用说明
原因
在网络上找了一圈也没有找到一个像样的说明。如果不是我们技术组的大大说这个东西可以用我都快放弃了。 稍微阅读了一下这个组件的源代码。发现该有的功能都有(如下所列)。 其实最初吸引我们用这个东西的功能是按文件更新。这种更新方式很好的解决了跨版本更新时。需要下载大量的重复文件的问题。 当然这种实现方式也有自己的问题,下面会有详细的解释
- 按文件更新
- 更新失败的时候,只更新失败的文件
- 更新失败的情况下,下次重新启动,只更新上次更新的错误文件
原理
本地会存在一个配置文件,网络中(你的服务器中也会存在一个配置文件)。通过本地配置文件与网络的配置文件进行比对。发现差异化数据然后,将网络数据拉取到本地。
使用
创建AssetsManagerEx
_assets_manager_ex = AssetsManagerEx::create("Config/project.manifest", FileUtils::getInstance()->getWritablePath() + "DownLoad");
_assets_manager_ex->retain();
注解
很明显这是创建了今天的主角AssetsManagerEx然后拿了他的指针。参数上第一个是本地的配置文件地地址,第二个参数是你从网络上拉取的数据的本地保存地址。
或许你现在想问,这个配置文件是什么格式的,我需要怎么来生成这个配置文件,配置文件格式如下
本地配置文件
{
"packageUrl" : "http://tools.itharbors.com/assets_manager/AMTestScene1/",
"remoteManifestUrl" : "http://7xs6k4.com1.z0.glb.clouddn.com/project.manifest",
"remoteVersionUrl" : "http://tools.itharbors.com/assets_manager/AMTestScene1/version_dev.manifest",
"version" : "1.0.0",
"engineVersion" : "3.0 beta", "assets" : {
"Images/background1.jpg" : {
"md5" : "..."
}
}, "searchPaths" : [
]
}
注解
这是一个Json的数据格式
packageUrl是你要下载具体内容的地址,程序允许的时候会将你的资源名称比如Image/xxx.png添加到packageUrl的后边组成完整的连接,相对于我们刚才举得例子的位置就是http://tools.itharbors.com/assets_manager/AMTestScene1/Image/xxx.png。然后从这个连接中拉取数据到本地并且保存为Image/xxx.png
remoteManifestUrl是远程的配置文件地址,与你本地的配置文件做为对应。就是最前边原理里边提到的拉取远程数据跟本地数据做对比的远程数据
remoteVersionUrl 因为配置文件中可能存在很多需要更新的配置文件的信息,所以频发的拉取这个数据是非常要命的。所以AssetsManagerEx提供了一个让你只拉取版本信息的连接,具体内容跟远程配置文件格式相似,只是没有了文件的相关配置,下面我会给出相应的范例
远程版本文件
{
"packageUrl": "http://tools.itharbors.com/assets_manager/AMTestScene1/",
"remoteManifestUrl": "http://tools.itharbors.com/assets_manager/AMTestScene1/project_dev.manifest",
"remoteVersionUrl": "http://tools.itharbors.com/assets_manager/AMTestScene1/version_dev.manifest",
"version": "1.2.0",
"engineVersion": "3.0 dev"
}
远程配置文件
{
"packageUrl" : "http://tools.itharbors.com/assets_manager/AMTestScene1/",
"remoteManifestUrl" : "http://7xs6k4.com1.z0.glb.clouddn.com/project_dev.manifest",
"remoteVersionUrl" : "http://tools.itharbors.com/assets_manager/AMTestScene1/version_dev.manifest",
"version" : "1.2.0",
"engineVersion" : "3.x dev", "assets" : {
"Images/assetMgrBackground1.jpg" : {
"md5" : "....."
},
"Images/ball.png" : {
"md5" : "..."
},
"Images/blocks.png" : {
"md5" : "..."
},
"compressed.zip" : {
"md5" : "...",
"compressed" : true
},
"Images/Bird.jpg" : {
"md5" : "..."
},
"Images/Daisy_Flower.jpg" : {
"md5" : "..."
},
"Images/Mountain_Reflections.jpg" : {
"md5" : "..."
},
"Images/Plitvice_National_Park.jpg" : {
"md5" : "..."
},
"Images/sakountala.jpg" : {
"md5" : "..."
},
"Images/Snake_River.jpg" : {
"md5" : "..."
},
"Images/Thunder.jpg" : {
"md5" : "..."
},
"Images/Tranquil_Lagoon.jpg" : {
"md5" : "..."
},
"Images/Tyrol.jpg" : {
"md5" : "..."
},
"Images/univ-lille1.jpg" : {
"md5" : "..."
},
"Images/Yellow_Garden_Flowers.jpg" : {
"md5" : "..."
},
"Images/Yellow_Lilly.jpg" : {
"md5" : "..."
},
"Images/Yellow_Tulips.jpg" : {
"md5" : "..."
}
}, "searchPaths" : [
]
}
业务相关的代码
if (!_assets_manager_ex->getLocalManifest()->isLoaded())
{
onLoadSuccess();
}
else
{
_assets_manager_listener = cocos2d::extension::EventListenerAssetsManagerEx::create(_assets_manager_ex, [this](EventAssetsManagerEx * event){
switch (event->getEventCode()) {
case cocos2d::extension::EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST:
case cocos2d::extension::EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST:
case cocos2d::extension::EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST:
case cocos2d::extension::EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS:
case cocos2d::extension::EventAssetsManagerEx::EventCode::UPDATE_FAILED:
{
this->onLoadError((int)event->getEventCode());
break;
} case cocos2d::extension::EventAssetsManagerEx::EventCode::ERROR_UPDATING:
{
tryDownloadFaildAssets(); break;
} case cocos2d::extension::EventAssetsManagerEx::EventCode::ASSET_UPDATED:
{
tryDownloadFaildAssets(); break;
} case cocos2d::extension::EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE:
{
CCLOG("已经是最新版本,直接进入主界面");
this->onAllFileIsNew();
break;
} case cocos2d::extension::EventAssetsManagerEx::EventCode::UPDATE_FINISHED:
{
CCLOG("更新完成重新加载");
this->onLoadSuccess();
break;
} case cocos2d::extension::EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION:
{
// this->onLoadPercent(event->getPercent());
this->onLoadPercent(event->getPercentByFile());
break;
} case cocos2d::extension::EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND:
{
CCLOG("发现新本版开始升级");
break;
} default:
break;
}
}); Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_assets_manager_listener, );
_assets_manager_ex->update();
}
代码注解
- 其实意思已经很明显了,就是查看一下当前是不是已经加载完成了。如果已经加载完成了,那么直接跳过,进入游戏就好了
- 如果没有加载完成就需要添加上监听器,然后启动下载,等待回调就好了
- 他的枚举的类型我就不一一进行解释了,自己看着字面上,应该可以猜到具体是什么。
- 其实说明到现在已经基本上没有啥好说的了。
更深入一些的东西(AssetsManagerEx已经实现的功能和具体的实现过程)
说明
其实之前的说明已经能够让你用起来了,不过他有些设计上的思路,我觉得还不错,所以专门来讲解一下原理。如果你觉得原理这种东西没有什么好了解的能用就行了。好的,请你跳过这一节,进入下一节,并且把它读完,因为不读完,(3.9版本肯定3.10应该)不能用。
进行比对
- 拉取配置文件到本地,然后添加.temp后缀作为临时文件
- 然后将临时文件跟本地文件进行比对,得出差异结果集,然后操作本地文件
- 这个文件比对是拿md5数据进行确定这个文件是否正常的。不过这个md5是指配置文件里边的md5而不是这个文件实际的md5
- 请你不要认为这套md5是文件的md5校验,把它认为是一个文件的版本号可能更容易理解一些
- 之所以文件用md5进行比对,应该是方便后台在构建这个文件时有一个可靠的依据
怎样进行不修改代码的情况下替换资源
- 通过添加SearchPath进行文件替换
- 其实组件并没有直接替换掉你包里边的文件,而只是添加了优先搜索目录来进行文件的优先查找的权限
下载文件的确立过程
- 这个组件会修改他自己下载的.temp组件,其实就是添加上下载的状态,然后重新保存一边
- 重新保存的时机在所有的文件尝试下载过一边之后
- 下载一边的意思是说成功和失败都算
- 但是下载过程中,软件意外退出或者主动退出,是不会保存状态的,如果需要,则需要业务进行手动的保存调用
如何做到重启之后,依然沿着上次的下载过程继续下载的
- 如果程序检测到存在.temp文件,并且.temp文件与远程文件的版本是一样的话,那么直接认为.temp文件是最新版本,尝试从.temp文件中尝试重新加载上一次的数据
- 也就是说,只有所有的文件下载过一遍之后,系统主动保存到.temp文件之后。再重新启动能够重新复盘
- 或者下载到一半,你觉得用户可能要退出的情况下保存了这个文件也能够复盘成功
修订
说明
- AssetsManagerEx在3.9的Demo上根本跑不起来
- 3.10没有测试过,我看了下源码的地方貌似也没有修改
BUG的表现
在任何一个资源下载失败的情况下,你会发现更新已经卡住再也不动了。
BUG的原因
这个组件的内部会有一个计数,未下载完的计数。组件在成功下载的时候回将这个计数减一。所有都成功的时候能正常的跑通。但是存在未下载完成的时候。这个计数没有减一,所以系统一直在等下载失败的数据下载完成。而下载失败的数据已经不再下载了,所以根本不会存在减一的情况。所以系统就会掉到这个BUG中,而造成业务链的断裂
修复
将AssetsManagerEx::onError方法修改成下边这个样子
void AssetsManagerEx::onError(const network::DownloadTask& task,
int errorCode,
int errorCodeInternal,
const std::string& errorStr)
{
// Skip version error occured
if (task.identifier == VERSION_ID)
{
CCLOG("AssetsManagerEx : Fail to download version file, step skipped\n");
_updateState = State::PREDOWNLOAD_MANIFEST;
downloadManifest();
}
else if (task.identifier == MANIFEST_ID)
{
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST, task.identifier, errorStr, errorCode, errorCodeInternal);
}
else
{
auto unitIt = _downloadUnits.find(task.identifier);
// Found unit and add it to failed units
if (unitIt != _downloadUnits.end())
{
--_totalWaitToDownload; DownloadUnit unit = unitIt->second;
_failedUnits.emplace(unit.customId, unit);
}
dispatchUpdateEvent(EventAssetsManagerEx::EventCode::ERROR_UPDATING, task.identifier, errorStr, errorCode, errorCodeInternal);
}
}
Demo
别以为下载了就好用,去看看修订这一章节然后你才能成功(仅限3.9)
下课,解散
AssetsManagerEx 组件使用说明的更多相关文章
- 从零开始编写自己的C#框架(23)——上传组件使用说明
文章导航 1.前言 2.上传组件功能说明 3.数据库结构 4.上传配置管理 5.上传组件所使用到的类 6.上传组件调用方法 7.效果演示 8.小结 1.前言 本系列所使用的是上传组件是大神July开发 ...
- vue2.0的瀑布流组件-使用说明
做一个小项目,需要瀑布流,就选他了,先看看效果 使用瀑布流布局组件:vue-waterfall-easy 下载引入: 方式一:直接从git上复制组件的完整代码,引入vue组件文件即可 import v ...
- stenciljs 学习十三 @stencil/router 组件使用说明
@stencil/router 组件包含的子组件 stencil-router stencil-route-switch stencil-route stencil-route-link stenci ...
- amazeui学习笔记二(进阶开发5)--Web 组件开发规范Rules
amazeui学习笔记二(进阶开发5)--Web 组件开发规范Rules 一.总结 1.见名知意:见那些class名字知意,见函数名知意,见文件名知意 例如(HISTORY.md Web 组件更新历史 ...
- APICloud AVM框架 封装车牌号输入键盘组件
AVM(Application-View-Model)前端组件化开发模式基于标准Web Components组件化思想,提供包含虚拟DOM和Runtime的编程框架avm.js以及多端统一编译工具,完 ...
- 从零开始编写自己的C#框架(1)——前言
记得十五年前自学编程时,拿着C语言厚厚的书,想要上机都不知道要用什么编译器来执行书中的例子.十二年前在大学自学ASP时,由于身边没有一位同学和朋友学习这种语言,也只能整天混在图收馆里拼命的啃书.而再后 ...
- web前端交互性易用性说明
总结一下我们在web前端开发过程中总是强调交互性.易用性的情况分析说明.个人觉得web前端的易用交互也就是我们所说人性化操作.不外乎希望达到的效果为:界面风格简洁明了.重点突出:操作简单,直观可见.当 ...
- 从零开始编写自己的C#框架 ---- 系列文章
目录: 从零开始编写自己的C#框架(1)——前言从零开始编写自己的C#框架(2)——开发前的准备工作从零开始编写自己的C#框架(3)——开发规范从零开始编写自己的C#框架(4)——文档编写说明从零开始 ...
- C#框架
从零开始编写自己的C#框架(1)——前言 记得十五年前自学编程时,拿着C语言厚厚的书,想要上机都不知道要用什么编译器来执行书中的例子.十二年前在大学自学ASP时,由于身边没有一位同学和朋友学习这种 ...
随机推荐
- poj1002-487-3279(字符串处理)
一,题意: 中文题,不解释!二,思路: 1,处理输入的电话号码 2,排序num[]数组 3,输出三,步骤: 1,消除 -.Q.Z 三种字符,将一个电话号码转化为一个整数存如num[]数组 如:num[ ...
- 关于Node.js的总结
Node是个啥? 1.Node 是一个服务器端 JavaScript 解释器,可是真的以为JavaScript不错的同学学习Node就能轻松拿下,那么你就错了,总结:水深不深我还不知道,不过确实不浅. ...
- C#_技巧:计算代码块运行的时间
System.Diagnostics下类Stopwatch,给程序代码块运行计时, 利用start()和stop()方法来标记代码快. 该命名空间下还有一些其他类,可以对程序进行诊断(diagnosi ...
- .NET Core与.NET Framework、Mono之间的关系
随着微软的.NET开源的推进,现在在.NET的实现上有了三个.NET Framework,Mono和.NET Core.经常被问起Mono的稳定性怎么样,后续Mono的前景如何,要回答这个问题就需要搞 ...
- 多种坐标系之间的转换 Proj.NET和DotSpatial
Proj.NET ( http://projnet.codeplex.com/)是一个.NET下开源的空间参照和投影引擎,遵循OGC相关标准.负责人(Coordinators )是D_Guidi 和S ...
- .net中事件引起的内存泄漏分析
系列主题:基于消息的软件架构模型演变 在Winform和Asp.net时代,事件被大量的应用在UI和后台交互的代码中.看下面的代码: private void BindEvent() { var bt ...
- MySQL 分区介绍总结
200 ? "200px" : this.width)!important;} --> 介绍 分区是指根据一定的规则将一个大表分解成多个更小的部分,这里的规则一般就是利用分区 ...
- ASP.NET MVC学前篇之扩展方法、链式编程
ASP.NET MVC学前篇之扩展方法.链式编程 前言 目的没有别的,就是介绍几点在ASP.NETMVC 用到C#语言特性,还有一些其他琐碎的知识点,强行的划分一个范围的话,只能说都跟MVC有关,有的 ...
- .NET组件程序设计之线程、并发管理(二)
.Net组件程序设计之线程.并发管理(二) 2.同步线程 手动同步 监视器 互斥 可等待事件 同步线程 所有的.NET组件都支持在多线程的环境中运行,可以被多个线程并发访问,如果没有线程同步,这样的后 ...
- 构建基于Chromium的应用程序
chromium是google chrome浏览器所采用的内核,最开始由苹果的webkit发展而出,由于webkit在发展上存在分歧,而google希望在开发上有更大的自由度,2013年google决 ...