原因

在网络上找了一圈也没有找到一个像样的说明。如果不是我们技术组的大大说这个东西可以用我都快放弃了。 稍微阅读了一下这个组件的源代码。发现该有的功能都有(如下所列)。 其实最初吸引我们用这个东西的功能是按文件更新。这种更新方式很好的解决了跨版本更新时。需要下载大量的重复文件的问题。 当然这种实现方式也有自己的问题,下面会有详细的解释

  1. 按文件更新
  2. 更新失败的时候,只更新失败的文件
  3. 更新失败的情况下,下次重新启动,只更新上次更新的错误文件

原理

本地会存在一个配置文件,网络中(你的服务器中也会存在一个配置文件)。通过本地配置文件与网络的配置文件进行比对。发现差异化数据然后,将网络数据拉取到本地。

使用

创建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();
}

代码注解

  1. 其实意思已经很明显了,就是查看一下当前是不是已经加载完成了。如果已经加载完成了,那么直接跳过,进入游戏就好了
  2. 如果没有加载完成就需要添加上监听器,然后启动下载,等待回调就好了
  3. 他的枚举的类型我就不一一进行解释了,自己看着字面上,应该可以猜到具体是什么。
  4. 其实说明到现在已经基本上没有啥好说的了。

更深入一些的东西(AssetsManagerEx已经实现的功能和具体的实现过程)

说明

其实之前的说明已经能够让你用起来了,不过他有些设计上的思路,我觉得还不错,所以专门来讲解一下原理。如果你觉得原理这种东西没有什么好了解的能用就行了。好的,请你跳过这一节,进入下一节,并且把它读完,因为不读完,(3.9版本肯定3.10应该)不能用。

进行比对

  1. 拉取配置文件到本地,然后添加.temp后缀作为临时文件
  2. 然后将临时文件跟本地文件进行比对,得出差异结果集,然后操作本地文件
  3. 这个文件比对是拿md5数据进行确定这个文件是否正常的。不过这个md5是指配置文件里边的md5而不是这个文件实际的md5
  4. 请你不要认为这套md5是文件的md5校验,把它认为是一个文件的版本号可能更容易理解一些
  5. 之所以文件用md5进行比对,应该是方便后台在构建这个文件时有一个可靠的依据

怎样进行不修改代码的情况下替换资源

  1. 通过添加SearchPath进行文件替换
  2. 其实组件并没有直接替换掉你包里边的文件,而只是添加了优先搜索目录来进行文件的优先查找的权限

下载文件的确立过程

  1. 这个组件会修改他自己下载的.temp组件,其实就是添加上下载的状态,然后重新保存一边
  2. 重新保存的时机在所有的文件尝试下载过一边之后
  3. 下载一边的意思是说成功和失败都算
  4. 但是下载过程中,软件意外退出或者主动退出,是不会保存状态的,如果需要,则需要业务进行手动的保存调用

如何做到重启之后,依然沿着上次的下载过程继续下载的

  1. 如果程序检测到存在.temp文件,并且.temp文件与远程文件的版本是一样的话,那么直接认为.temp文件是最新版本,尝试从.temp文件中尝试重新加载上一次的数据
  2. 也就是说,只有所有的文件下载过一遍之后,系统主动保存到.temp文件之后。再重新启动能够重新复盘
  3. 或者下载到一半,你觉得用户可能要退出的情况下保存了这个文件也能够复盘成功

修订

说明

  1. AssetsManagerEx在3.9的Demo上根本跑不起来
  2. 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)

AssetsManagerExTest.zip

下课,解散

AssetsManagerEx 组件使用说明的更多相关文章

  1. 从零开始编写自己的C#框架(23)——上传组件使用说明

    文章导航 1.前言 2.上传组件功能说明 3.数据库结构 4.上传配置管理 5.上传组件所使用到的类 6.上传组件调用方法 7.效果演示 8.小结 1.前言 本系列所使用的是上传组件是大神July开发 ...

  2. vue2.0的瀑布流组件-使用说明

    做一个小项目,需要瀑布流,就选他了,先看看效果 使用瀑布流布局组件:vue-waterfall-easy 下载引入: 方式一:直接从git上复制组件的完整代码,引入vue组件文件即可 import v ...

  3. stenciljs 学习十三 @stencil/router 组件使用说明

    @stencil/router 组件包含的子组件 stencil-router stencil-route-switch stencil-route stencil-route-link stenci ...

  4. amazeui学习笔记二(进阶开发5)--Web 组件开发规范Rules

    amazeui学习笔记二(进阶开发5)--Web 组件开发规范Rules 一.总结 1.见名知意:见那些class名字知意,见函数名知意,见文件名知意 例如(HISTORY.md Web 组件更新历史 ...

  5. APICloud AVM框架 封装车牌号输入键盘组件

    AVM(Application-View-Model)前端组件化开发模式基于标准Web Components组件化思想,提供包含虚拟DOM和Runtime的编程框架avm.js以及多端统一编译工具,完 ...

  6. 从零开始编写自己的C#框架(1)——前言

    记得十五年前自学编程时,拿着C语言厚厚的书,想要上机都不知道要用什么编译器来执行书中的例子.十二年前在大学自学ASP时,由于身边没有一位同学和朋友学习这种语言,也只能整天混在图收馆里拼命的啃书.而再后 ...

  7. web前端交互性易用性说明

    总结一下我们在web前端开发过程中总是强调交互性.易用性的情况分析说明.个人觉得web前端的易用交互也就是我们所说人性化操作.不外乎希望达到的效果为:界面风格简洁明了.重点突出:操作简单,直观可见.当 ...

  8. 从零开始编写自己的C#框架 ---- 系列文章

    目录: 从零开始编写自己的C#框架(1)——前言从零开始编写自己的C#框架(2)——开发前的准备工作从零开始编写自己的C#框架(3)——开发规范从零开始编写自己的C#框架(4)——文档编写说明从零开始 ...

  9. C#框架

    从零开始编写自己的C#框架(1)——前言   记得十五年前自学编程时,拿着C语言厚厚的书,想要上机都不知道要用什么编译器来执行书中的例子.十二年前在大学自学ASP时,由于身边没有一位同学和朋友学习这种 ...

随机推荐

  1. ReactJS入门(一)—— 初步认识React

    React刚开始红的时候,由于对其不甚了解,觉得JSX的写法略非主流,故一直没打算将其应用在项目上,随着身边大神们的科普,才后知后觉是个好东西. 好在哪里呢?个人拙见,有俩点: 1. 虚拟DOM —— ...

  2. 剑指Offer面试题:10.数值的整数次方

    一.题目:数值的整数次方 题目:实现函数double Power(doublebase, int exponent),求base的exponent次方.不得使用库函数,同时不需要考虑大数问题. 在.N ...

  3. LeetCode Note 1st,practice makes perfect

    1. Two Sum Given an array of integers, return indices of the two numbers such that they add up to a ...

  4. 初学者--bootstrap(三)下载与安装----在路上(7)

    ----------------------------------------对于学习与工作者来说用压缩版,若是研究框架则用源代码---------------------------------- ...

  5. iOS-在团队开发过程中控制代码版本

    Cornerstone Svn简单使用指南: -- what if 负责编写 一.安装并拷贝项目 1.第一步:安装svn.2.第二步:第一个使用svn,找到“Check Out Working Cop ...

  6. iOS-多线程基础

    进程与线程: 1>   一个应用程序对应一个进程,一个进程帮助程序占据一块存储空间 2>   要想在进程中执行任务,就必须开启线程,一条线程就代表一个任务 3>   一个进程中允许开 ...

  7. mfc的OnInitDialog的返回值

    以前从未注意过初始化函数的返回值,今天看到书中所述,以后可能用得上. OnInitDialog的返回值告诉windows如何处置输入焦点,如果返回 TRUE,则windows将输入焦点指派给制表键控制 ...

  8. Sql Server系列:流程控制语句

    T-SQL中用来编写流程控制模块的语句有:BEGIN...AND语句.IF...ELSE语句.CASE语句.WHILE语句.GOTO语句.BREAK语句.WAITFOR语句和RETURN语句. 1 B ...

  9. Android之线程池深度剖析

    1.线程池的引入   引入的好处:   1)提升性能.创建和消耗对象费时费CPU资源   2)防止内存过度消耗.控制活动线程的数量,防止并发线程过多.   使用条件:      假设在一台服务器完成一 ...

  10. XSD文件生成C#VO实体类

    最近公司要做一个项目,需要和现有的其他项目对接,由于不知道他们的数据库,只有XSD文件.所以,我们在修改相应的程序时,就需要根据他们提供的XSD文件,来写我们的VO实体类,由于我写过根据Oracle数 ...