原因

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

  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. c++ eof()函数

    C++ eof()函数可以帮助我们用来判断文件是否为空,抑或是判断其是否读到文件结尾.在这里我们将会对其进行详细的介绍. C++编程语言中的很多功能在我们的实际应用中起着非常大的作用.比如在对文件文本 ...

  2. 使用NServiceBus开发分布式应用

    系列主题:基于消息的软件架构模型演变 NServiceBus 是一个.Net平台下开源的消息服务框架,这类产品有时也被称作ESB(Enterprise Service Bus)--企业服务总线.NSe ...

  3. 给Macbook Pro更换固态硬盘并转移系统的最简单办法

    没有固态硬盘,再快的CPU,再强悍的显卡,都是白搭,由于“木桶原理”,瓶颈就在这里啊.如今的固态硬盘价格跌了很多,我记得去年我买的120G的固态硬盘还要将近600元,而现在只需要不到400了.   我 ...

  4. [SDK2.2]Windows Azure Storage (16) 使用WCF服务,将本地图片上传至Azure Storage (上) 客户端代码

    <Windows Azure Platform 系列文章目录> 前一章我们完成了服务器端的代码,并且已经发布到了Windows Azure云端. 本章我们将实现客户端的代码,客户端这里我们 ...

  5. C# 用原生JS进行文件的上传

    1.此文章是用原生JS来进行文件的上传,有两个版本,一个不用ajax,一个用ajax. 1)非AJAX <!DOCTYPE html> <html> <head> ...

  6. iOS-----Crash文件分析(一)

    开发程序的过程中不管我们已经如何小心,总是会在不经意间遇到程序闪退.脑补一下当你在一群人面前自信的拿着你的App做功能预演的时候,流畅的操作被无情地Crash打断.联想起老罗在发布Smartisan ...

  7. [转]Linux常用命令

    系统信息arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS / ...

  8. 浅谈ThinkPHP3.2的子域名部署和路由优化(一)

    前言:建立一个网站系统,往往包含多个子网站,例如PC官网,移动端官网,后台管理,数据源自一个相同的数据库,整个架构上,从ThinkPHP来看,可以大体理解为Model(M)是一样的,Controlle ...

  9. javascript技术难点(三)之this、new、apply和call详解

    4)    this.new.call和apply的相关问题 讲解this指针的原理是个很复杂的问题,如果我们从javascript里this的实现机制来说明this,很多朋友可能会越来越糊涂,因此本 ...

  10. Atom支持Markdown和Latex

    本篇博客主要用于记录Atom编辑器同时支持markdown和latex: 1.安装 安装方法1: (Windows系统)File->Settings->Install中搜索markdown ...