LoaderManager 类用于用于批量加载资源。此类是单例,不要手动实例化此类,请通过Laya.loader访问。全部队列加载完成,会派发 Event.COMPLETE 事件;如果队列中任意一个加载失败,会派发 Event.ERROR 事件,事件回调参数值为加载出错的资源地址。

LoaderManager 类提供了以下几种功能: 多线程:默认5个加载线程,可以通过maxLoader属性修改线程数量; 多优先级:有0-4共5个优先级,优先级高的优先加载。0最高,4最低; 重复过滤:自动过滤重复加载(不会有多个相同地址的资源同时加载)以及复用缓存资源,防止重复加载; 错误重试:资源加载失败后,会重试加载(以最低优先级插入加载队列),retryNum设定加载失败后重试次数,retryDelay设定加载重试的时间间隔。

LoaderManager是Laya加载资源的统一入口,管理laya的加载器(Laya.Loader),负责控制加载优先级,加载线程维护与上限上线控制,加载失败后重试等操作。在laya中使用的Laya.loader是LoaderManager的实例,而不是Laya.Loader的实例。

源码阅读

属性简介
		/**@private */
private static var _resMap:Object = {}; //@资源map,url做key,resInfo为value, 用于检测资源是否已经在队列中,防止重复加载。
/**@private */
public static var createMap:Object = {atlas: [null, Loader.ATLAS]}; //@用于检测create方法的类型。 /** 加载出错后的重试次数,默认重试一次*/
public var retryNum:int = 1;
/** 延迟时间多久再进行错误重试,默认立即重试*/
public var retryDelay:int = 0;
/** 最大下载线程,默认为5个*/
public var maxLoader:int = 5; /**@private */
private var _loaders:Array = []; //@缓存的loader列表
/**@private */
private var _loaderCount:int = 0; //@正在使用的loader数量、加载线程数量
/**@private */
private var _resInfos:Array = []; //@加载资源列表,存放所有需要加载的资源,按优先级和队列顺序排列 /**@private */
private var _infoPool:Array = []; //@加载数据对象的缓存池
/**@private */
private var _maxPriority:int = 5; //@优先级数量 0-4
/**@private */
private var _failRes:Object = {}; //@失败资源的加载次数,用于失败后重试计数
初始化
  • LoaderManager在Laya.init时创建,赋值给Laya.loader。
  • 按照优先级,创建5(_maxPriority)个队列,用于存放需要加载的资源数据。队列的索引就是加载的优先级,0-4,数字越小,优先级越高。加载时,按照优先级和队列顺序依次加载。
		/**
* <p>创建一个新的 <code>LoaderManager</code> 实例。</p>
* <p><b>注意:</b>请使用Laya.loader加载资源,这是一个单例,不要手动实例化此类,否则会导致不可预料的问题。</p>
*/
public function LoaderManager() {
for (var i:int = 0; i < this._maxPriority; i++) this._resInfos[i] = [];is._resInfos[i] = [];
}
单个资源加载流程
  • 判断资源是否存在,如果存在则直接调用progress和complete。加载的资源存放在Laya.Loader中,不在LoaderManager中。
  • 判断资源是否在加载队列中:
    • 如果存在,则监听COMPLETE和PROFRESS事件。注:添加时不能设置offBefore,防止后续加载的资源的回调覆盖之前的加载完成事件。
    • 如果不存在,则创建ResInfo,将info放到对应优先级队列的末尾,监听COMPLETE事件。调用next方法,尝试加载。
  • 检测当前正在使用的loader的数量,如果数量不足maxLoader个数,则按优先级查找队列,再取队列的第一个数据去加载。如果当前正在使用的loader的count为0,则触发Laya.loader(LoaderManager)的COMPLETE事件。
  • 加载是使用Laya.Loader执行加载逻辑。
  • 加载完成后(或者失败),回收当前loader,更新loader数量,如果加载失败,则重新放入加载队列,并且优先级设置为最低。如果失败多次,则出发Error事件,将url传递出去。加载成功或者失败都会出发COMPLETE事件,调用complete方法。清理和回收相关数据。
  • 尝试调用next,尝试选择下一个资源加载。
		/**
* <p>加载资源。资源加载错误时,本对象会派发 Event.ERROR 事件,事件回调参数值为加载出错的资源地址。</p>
* <p>因为返回值为 LoaderManager 对象本身,所以可以使用如下语法:Laya.loader.load(...).load(...);</p>
* @param url 要加载的单个资源地址或资源信息数组。比如:简单数组:["a.png","b.png"];复杂数组[{url:"a.png",type:Loader.IMAGE,size:100,priority:1},{url:"b.json",type:Loader.JSON,size:50,priority:1}]。
* @param complete 加载结束回调。根据url类型不同分为2种情况:1. url为String类型,也就是单个资源地址,如果加载成功,则回调参数值为加载完成的资源,否则为null;2. url为数组类型,指定了一组要加载的资源,如果全部加载成功,则回调参数值为true,否则为false。
* @param progress 加载进度回调。回调参数值为当前资源的加载进度信息(0-1)。
* @param type 资源类型。比如:Loader.IMAGE。
* @param priority (default = 1)加载的优先级,优先级高的优先加载。有0-4共5个优先级,0最高,4最低。
* @param cache 是否缓存加载结果。
* @param group 分组,方便对资源进行管理。
* @param ignoreCache 是否忽略缓存,强制重新加载。
* @return 此 LoaderManager 对象本身。
*/
public function load(url:*, complete:Handler = null, progress:Handler = null, type:String = null, priority:int = 1, cache:Boolean = true, group:String = null, ignoreCache:Boolean = false):LoaderManager {
if (url is Array) return _loadAssets(url as Array, complete, progress, type, priority, cache, group);
var content:* = Loader.getRes(url);
if (content != null) {
//增加延迟回掉
Laya.timer.frameOnce(1, null, function():void {
progress && progress.runWith(1);
complete && complete.runWith(content);
//判断是否全部加载,如果是则抛出complete事件
_loaderCount || event(Event.COMPLETE);
});
} else {
var info:ResInfo = _resMap[url];
if (!info) {
info = _infoPool.length ? _infoPool.pop() : new ResInfo();
info.url = url;
info.type = type;
info.cache = cache;
info.group = group;
info.ignoreCache = ignoreCache;
//@收到ResInfo的COMPLETE事件则执行complete方法
complete && info.on(Event.COMPLETE, complete.caller, complete.method, complete.args);
progress && info.on(Event.PROGRESS, progress.caller, progress.method, progress.args);
_resMap[url] = info;
//@选择对应优先级队列
priority = priority < this._maxPriority ? priority : this._maxPriority - 1;
this._resInfos[priority].push(info);
_next();
} else {
//@如果已经在加载队列,则只注册事件,不创建加载数据。
//@注:这里offBefore设置为false,防止事件被覆盖,导致complete执行不全。
complete && info._createListener(Event.COMPLETE, complete.caller, complete.method, complete.args, false, false);
progress && info._createListener(Event.PROGRESS, progress.caller, progress.method, progress.args, false, false);
}
}
return this;
} private function _next():void {
//@loader满了则返回等待空闲loader
if (this._loaderCount >= this.maxLoader) return;
//@按优先级查找列表
for (var i:int = 0; i < this._maxPriority; i++) {
var infos:Array = this._resInfos[i];
while (infos.length > 0) {
var info:ResInfo = infos.shift();
if (info) return _doLoad(info);
}
}
//@所有loader都空闲,则触发LoaderManager的加载完成
_loaderCount || event(Event.COMPLETE);
} private function _doLoad(resInfo:ResInfo):void {
this._loaderCount++;
var loader:Loader = this._loaders.length ? this._loaders.pop() : new Loader();
loader.on(Event.COMPLETE, null, onLoaded);
loader.on(Event.PROGRESS, null, function(num:Number):void {
resInfo.event(Event.PROGRESS, num);
});
loader.on(Event.ERROR, null, function(msg:*):void {
onLoaded(null);
}); var _this:LoaderManager = this;
function onLoaded(data:* = null):void {
//@清理、回收loader
loader.offAll();
loader._data = null;
loader._customParse = false;
_this._loaders.push(loader);
_this._endLoad(resInfo, data is Array ? [data] : data);
_this._loaderCount--;
_this._next();
}
//@具体加载流程在Loader中实现
loader._class = resInfo.clas;
loader.load(resInfo.url, resInfo.type, resInfo.cache, resInfo.group, resInfo.ignoreCache);
} private function _endLoad(resInfo:ResInfo, content:*):void {
//如果加载后为空,放入队列末尾重试
var url:String = resInfo.url;
if (content == null) {
var errorCount:int = this._failRes[url] || 0;
//@记录失败次数
if (errorCount < this.retryNum) {
console.warn("[warn]Retry to load:", url);
this._failRes[url] = errorCount + 1;
//@重新放回加载队列
Laya.timer.once(retryDelay, this, _addReTry, [resInfo], false);
return;
} else {
//超过retryNum则直接抛出ERROR事件
console.warn("[error]Failed to load:", url);
event(Event.ERROR, url);
}
}
if (_failRes[url]) _failRes[url] = 0;
delete _resMap[url];
//@通知info执行complete方法
resInfo.event(Event.COMPLETE, content);
resInfo.offAll();
_infoPool.push(resInfo);
} private function _addReTry(resInfo:ResInfo):void {
this._resInfos[this._maxPriority - 1].push(resInfo);
_next();
}
加载多个资源流程

LoaderManager.load支持传入简单类型数组,比如:简单数组:["a.png","b.png"];复杂数组[{url:"a.png",type:Loader.IMAGE,size:100,priority:1},{url:"b.json",type:Loader.JSON,size:50,priority:1}]。

  • 遍历所有数组,记录总数,用于计算progress。记录下载的总数。
  • 分别调用load方法,加载每一个元素。
  • 每加载完一个资源,检查加载的数量是否等于本次的总数量,如果是,则执行complete操作。
		/**
* @private
* 加载数组里面的资源。
* @param arr 简单:["a.png","b.png"],复杂[{url:"a.png",type:Loader.IMAGE,size:100,priority:1},{url:"b.json",type:Loader.JSON,size:50,priority:1}]*/
private function _loadAssets(arr:Array, complete:Handler = null, progress:Handler = null, type:String = null, priority:int = 1, cache:Boolean = true, group:String = null):LoaderManager {
var itemCount:int = arr.length;
var loadedCount:int = 0;
var totalSize:int = 0;
var items:Array = [];
var success:Boolean = true;
for (var i:int = 0; i < itemCount; i++) {
var item:Object = arr[i];
if (item is String) item = {url: item, type: type, size: 1, priority: priority};
if (!item.size) item.size = 1;
item.progress = 0;
totalSize += item.size;
items.push(item);
var progressHandler:* = progress ? Handler.create(null, loadProgress, [item], false) : null;
var completeHandler:* = (complete || progress) ? Handler.create(null, loadComplete, [item]) : null;
load(item.url, completeHandler, progressHandler, item.type, item.priority || 1, cache, item.group || group);
} function loadComplete(item:Object, content:* = null):void {
loadedCount++;
item.progress = 1;
if (!content) success = false;
if (loadedCount === itemCount && complete) {
complete.runWith(success);
}
} function loadProgress(item:Object, value:Number):void {
if (progress != null) {
item.progress = value;
var num:Number = 0;
for (var j:int = 0; j < items.length; j++) {
var item1:Object = items[j];
num += item1.size * item1.progress;
}
var v:Number = num / totalSize;
progress.runWith(v);
}
}
return this;
}
create方法

根据clas类型创建一个未初始化资源的对象,随后进行异步加载,资源加载完成后,初始化对象的资源,并通过此对象派发 Event.LOADED 事件,事件回调参数值为此对象本身。套嵌资源的子资源会保留资源路径"?"后的部分。

如果url为数组,返回true;否则返回指定的资源类对象,可以通过侦听此对象的 Event.LOADED 事件来判断资源是否已经加载完毕。

注意:cache参数只能对文件后缀为atlas的资源进行缓存控制,其他资源会忽略缓存,强制重新加载。

  • create方法可以先创建一个指定类型的壳,同步返回给调用者。壳里的资源则通过异步的方式加载。

  • create方法主要是在Laya3D中使用,主要是Laya内部组件使用。使用create创建的类型有严格限制,在2D下支持Atlas,在3D有一定扩展。

  • 这些类型都要实现ICreateResource方法,提供onAsynLoaded,_setUrl等方法。如果创建一些不支持的类型,会直接抛出异常。

  • 同load方法一样,支持传入一个或者多个资源地址。

  • 使用create方法加载Atlas资源时,会返回null。

  • Laya3D所支持的类型:

    //在Laya3D初始化时设置
var createMap:Object = LoaderManager.createMap;
createMap["lh"] = [Sprite3D, Laya3D.HIERARCHY];
createMap["ls"] = [Scene, Laya3D.HIERARCHY];
createMap["lm"] = [Mesh, Laya3D.MESH];
createMap["lmat"] = [StandardMaterial, Laya3D.MATERIAL];
createMap["lpbr"] = [PBRMaterial, Laya3D.MATERIAL];
createMap["ltc"] = [TextureCube, Laya3D.TEXTURECUBE];
createMap["jpg"] = [Texture2D, "nativeimage"];
createMap["jpeg"] = [Texture2D, "nativeimage"];
createMap["png"] = [Texture2D, "nativeimage"];
createMap["pkm"] = [Texture2D, Loader.BUFFER];
createMap["lsani"] = [AnimationTemplet, Loader.BUFFER];
createMap["lrani"] = [AnimationTemplet, Loader.BUFFER];
createMap["raw"] = [DataTexture2D, Loader.BUFFER];
createMap["mipmaps"] = [DataTexture2D, Loader.BUFFER];
createMap["thdata"] = [TerrainHeightData, Loader.BUFFER];
createMap["lt"] = [TerrainRes, Laya3D.TERRAIN];
createMap["lani"] = [AnimationClip, Loader.BUFFER];
createMap["lav"] = [Avatar, Loader.JSON];
createMap["ani"] = [AnimationTemplet, Loader.BUFFER];//兼容接口

Laya LoaderManager小记的更多相关文章

  1. Laya资源加载小记

    Laya.Loader负责资源的加载逻辑,被LoaderManager管理. Laya支持多种类型资源加载,也支持自定义类型加载.不同类型的加载方式可能不同. Laya.Loader缓存已经被加载过得 ...

  2. [原]Paste.deploy 与 WSGI, keystone 小记

    Paste.deploy 与 WSGI, keystone 小记 名词解释: Paste.deploy 是一个WSGI工具包,用于更方便的管理WSGI应用, 可以通过配置文件,将WSGI应用加载起来. ...

  3. MySql 小记

    MySql  简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...

  4. Git小记

    Git简~介 Git是一个分布式版本控制系统,其他的版本控制系统我只用过SVN,但用的时间不长.大家都知道,分布式的好处多多,而且分布式已经包含了集中式的几乎所有功能.Linus创造Git的传奇经历就 ...

  5. 广州PostgreSQL用户会技术交流会小记 2015-9-19

    广州PostgreSQL用户会技术交流会小记 2015-9-19 今天去了广州PostgreSQL用户会组织的技术交流会 分别有两个session 第一个讲师介绍了他公司使用PostgreSQL-X2 ...

  6. 东哥读书小记 之 《MacTalk人生元编程》

         一直以来的自我感觉:自己是个记性偏弱的人.反正从小读书就喜欢做笔记(可自己的字写得巨丑无比,尼玛不科学呀),抄书这事儿真的就常发生俺的身上. 因为那时经常要背诵课文之类,反正为了怕自己忘记, ...

  7. Paypal支付小记

    Paypal支付小记 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...

  8. linux 下cmake 编译 ,调用,调试 poco 1.6.0 小记

    上篇文章 小记了: 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记. http://www.cnblogs.com/bleachli/p/4352 ...

  9. mongodb入门学习小记

    Mongodb 简单入门(个人学习小记) 1.安装并注册成服务:(示例) E:\DevTools\mongodb3.2.6\bin>mongod.exe --bind_ip 127.0.0.1 ...

随机推荐

  1. 从0开始搭建Element项目

    第一步:安装 Node.js/NPM 下载Node.js:https://nodejs.org/zh-cn/download/ 下载安装即可. 第二步:安装 vue-cli 打开 cmd 创建,在命令 ...

  2. 纯css3云彩动画效果

      效果描述: 纯CSS3实现的云彩动画飘动效果 非常逼真实用 使用方法: 1.将body中的代码部分拷贝到你的页面中 2.引入对应的CSS文件即可

  3. JavaScript实现限时抢购实例

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  4. 【转】基于easyui开发Web版Activiti流程定制器详解(一)——目录结构

    题外话(可略过): 前一段时间(要是没记错的话应该是3个月以前)发布了一个更新版本,很多人说没有文档看着比较困难,所以打算拿点时间出来详细给大家讲解一下,由于本人平时要工作还要陪老婆和孩子而且还经营着 ...

  5. jupyter notebook设置主题背景,字体和扩展插件

    windows上安装Anaconda (IPython notebook) Anaconda是一个包与环境的管理器,一个Python发行版,以及一个超过1000多个开源包的集合.它是免费和易于安装的, ...

  6. ab网站压力测试命令的参数、输出结果的中文注解

    ab命令原理 Apache的ab命令模拟多线程并发请求,测试服务器负载压力,也可以测试nginx.lighthttp.IIS等其它Web服务器的压力. ab命令对发出负载的计算机要求很低,既不会占用很 ...

  7. Elasticsearch + logstash + kibana 配置

    Elasticsearch 配置 Elasticsearch不仅仅是Lucene和全文搜索,我们还能这样去描述它: 分布式的实时文件存储,每个字段都被索引并可被搜索 分布式的实时分析搜索引擎 可以扩展 ...

  8. 在任务管理器中显示所有CPU内核性能

    在Windows7"任务管理器"的”性能“选项卡默认显示所有的CPU内核性能 在Windows10中可以通过设置来实现效果

  9. nuxt 脚手架创建nuxt项目中不支持es6语法的解决方案

    node本身并不支持es6语法,我们通常在vue项目中使用es6语法,是因为,我们使用babel做过处理, 为了让项目支持es6语法,我们必须同时使用babel 去启动我们的程序,所以再启动程序中加 ...

  10. salt常用命令(一)

    查看模块包含哪些函数 salt 'node' sys.list_functions test 查看函数的用法 salt 'node' sys.doc test.echo 使用模块中的函数 salt ' ...