QCon全球软件开发大会8折优惠倒计时,购票立减¥1760!了解详情 >>> 了解详情
写点什么

Flash 务实主义(六)——Loading

2011 年 5 月 26 日

加载形式

FLASH 加载文件有两种常规方式:一种是 URLLoader,可以加载文本、二进制数据或 URL 编码变量形式的数据,然后转换成简单的文本形式或值对字符串形式;一种是 Loader,可以将加载的图像文件转换成 BitmapData,也可以解析 SWF 文件。(Socket, LocalConnection 这些非常用类不在讨论范围)

加载的资源应被统一管理以方便调用和重用,管理方式一般有两种:一种是将资源全部打包进 SWF;一种是加载分散资源通过配置进行管理。我将对这两种管理方式进行介绍。

资源打包成 SWF

在编辑 FLA 文件时,我们可以导入各种图片,并为其设置链接名。除手工逐个操作外,我们可以借助 JSFL 进行自动化处理(请参考 http://bbs.9ria.com/thread-31730-1-1.html )。

由此生成 SWF 后,再用 Loader 加载。这时候我们需要的不再是舞台上的内容,而是 SWF 应用域里包含资源的类。可以用 Loader 的 contentLoaderInfo.applicationDomain.getDefinition() 方法来获得这个类,并实例化。如果在执行加载方法时第二个参数 LoaderContext 设置成了 ApplicationDomain.currentDomain,使得被加载的 SWF 与主 SWF 共享同一应用域,那么直接使用 getDefinitionByName () 方法也可以获得这个类。

通过 new 的方式就可以实例化获得的 BitmapData 或 MovieClip 类,然后使用。因此项目中要想得到一个资源,只要知道它的链接名即可。链接名是自行设定的有意义的名字,完全可以当做资源的唯一 ID。

需要注意的是,new 的过程就是图片解压缩的过程。处于 Class 状态时,图片占用的内存和 SWF 文件中这个图片占用的磁盘空间一致,而一旦通过 new 解压成无压缩的 BitmapData 后,占用的内存会急剧增加。不管是 PNG、JPG,还是矢量动画,new 之后的体积都会比原来大得多,因此不要随便将资源实例化后暂存。这个实例化过程理所当然是比较费时的,可能会出现卡的现象,但预先实例化,内存占用上是有很大区别的。

此外,如果选择设置 LoaderContext 使得全部资源加载到同一个域的话,有冲突的链接名是以先来先到的原则处理,即如果两个资源链接名相同,以先加载的对象为准。

打包成 SWF 有一个优点,SWF 可以让 JPEG 支持透明通道。一般来说,JPEG 压缩率高而不支持透明通道,PNG 压缩率低支持透明通道。将 PNG 导入 FLA 然后设置成 JPEG 压缩后,就能在压缩的同时保留透明通道,可以让支持透明通道的图片体积大大减小。

打包成 SWF 后,加载快且易于管理,是推荐方式。但这种做法限定你必须一次性加载所有资源,不能按需加载,有一定的局限性。比较适合加载 UI 皮肤,以及需要立即显示的图标等等。

还有一点需要注意:SWF 舞台上的内容,即使不显示出来也会消耗资源,因此请务必保证在发布时舞台为空。

资源分散加载

如果文件要按需加载,或者不希望用 SWF 打包增加维护成本,或者有大量文本以致于不能用 FLA 导入,那我们只能逐个文件加载。

因为资源可能处于不同目录,命名也不规范,也会有扩展名,这样的路径在代码中作为 ID 存在是不合适的。所以一般都会有一个文本配置文件,将这些文件路径和一个名称对应起来,并提供给模块加载。加载完成后则是通过这个名称来获取资源。

不要直接用 Loader 加载文件

不同文件有不同的加载方式,文本和二进制文件只能通过 URLLoader 加载,而 PNG、JPG、SWF 等文件则可以通过 Loader 和 URLLoader 两种方式加载。如果资源需要长期保存,建议全部用 URLLoader 方式加载,在需要获取资源时,再通过 Loader 的 loadBytes 方法解析已经加载的二进制数据,之后再显示。

这样做目的是为了节省内存,因为 Loader 加载的资源会自动实例化(解码),PNG、JPG 会展开成无压缩的 BitmapData,SWF 舞台的内容也会全部实例化,他们会占用大量内存。先用 URLLoader 将他们作为二进制数据加载,需要时再解码实例化,就不会出现这个问题。

并发加载

多文件加载还有一个问题:浏览器对并发下载数有限制,而这个限制和 Flash Player 的机制有冲突,所以一般情况下 Flash Player 同时发起的加载请求数最好不要超过 5 个, 否则加载事件可能会失效。为了解决这个问题,大部分人的解决方案都是采取队列加载,一次只加载一个文件。这在文件数量较小、单文件体积较大时并没有问题,但是当文件数量多、单文件体积小时,由于每次加载完一个文件后,重新请求下一个文件时需要等待服务器响应一段时间才开始加载,这样会浪费很多带宽,文件数量多时这个缺陷不能忽略,最多可能消耗 2 至 3 倍的加载时间。

为了解决这个问题,我们需要一种特殊的队列加载模块,可以同时加载,但是同时加载的文件数量不能超过某个值。基本思路就是在加载完一个文件后,检查正在进行加载的文件数量,小于定值就取队列中的下一个地址新建加载,否则就什么都不做。

BulkLoader( http://code.google.com/p/bulk-loader ),

LoaderMax( http://www.greensock.com/loadermax

都提供了这个功能,当然我的 GhostCat 也通过 AssetManager(复合 QueueLoadOper 实现)提供了这个功能。复杂度实际上并没有比线性加载高多少。

这样做,加载时等待服务器返回时依然有文件在下载,多个加载过程会平衡消耗带宽,带宽就不会被浪费。一般同时加载数量为 2 就足够了。但如果你的文件特别零散,使得两个文件同时等待返回的几率也很高,也可以考虑设置 2 以上的值。

哈希表缓存

当加载文件数量特别大的时候(诸如数百个),注意不要只使用数组保存。你可以创建一个 Dictionary,然后将名称作为键加载内容作为值,做一个哈希表,以后都直接通过名称从这个哈希表取值,会比遍历数组查找名称快很多。

使用 ZIP 或其他打包形式

如果你不喜欢 SWF 这种打包方式,也可以选择 ZIP 打包,详情请阅读 http://nochump.com/blog/archives/15

ZIP 的优点是可以用 winrar 打开,不需要借助专门的工具,缺点则是解压需要时间。当然,你也可以考虑用二进制自定义一个封装格式,这样也能加密资源文件,但这需要你自己编写一个管理工具。当然,这也不算特别麻烦。

使用 ShareObject 缓存

虽然有浏览器缓存,但实际上这种缓存持续不了几天,因为浏览器一向都有最大缓存限制。一般你看几个视频,这个缓存空间就消耗得差不多了。为了不让 Flash 加载的文件缓存被冲洗掉,你可以将加载的文件的二进制数据(Loader 是 contentLoaderInfo.bytes,URLLoader 则要用二进制方式加载获取其 data 属性)保存在 ShareObject 里,并添加版本号以便更新,下次加载就直接取这个数据。这个操作会请求大量 ShareObject 空间,因此 FLASH 会弹出提示让用户确认。如果你担心用户不确认,可以在游戏其他地方向用户说明情况并要求他们点击确认按钮。

现在不少游戏都采用了这种做法,效果还是可以的。

显示总体加载进度

我们可能在开始时会加载多个文件。比起每加载一个文件显示一次进度条从 0% 到 100% 的过程,显然是显示所有文件总体的加载进度,只进行一次从 0% 到 100% 的过程更具有实际意义。但是 FLASH 做这件事情并不太容易,因为它想要获得一个文件的大小就必须去加载它,而这个加载需要时间,你没有办法从一开始就立即获得所有文件的大小。

实现的方法只有一个,就是在程序中或者配置中写死所有文件的总大小,然后根据所有文件已经加载字节数的总和来计算和显示进度。

至于这个总和如何获得,可以在程序中写上 trace,实际加载运行一次便能获得实际所有文件大小总和,也可以做专门工具计算,也可以用操作系统来直接查看总和,总之方法不是问题。而且,即使这个值不准确也无大碍,因为加载流程依然是按加载完所有文件作为依据,而这个进度只是用来显示,就算不准也不过是未到 100% 就结束或者到了 100% 也要等待一段时间才能结束。这无伤大雅。

之前所说的类库都提供了这样的功能。

主 SWF 加载问题

SWF 必须加载完所有类后才能开始运行并显示图像,这样一来,第一个主 SWF 加载自己时就无法显示加载进度。解决这个问题有两种办法:

一种办法很老但是实用,就是创建一个小 SWF 先显示出来,专门用来加载主 SWF,主 SWF 加载完毕后它就完成了使命。实际上这并不麻烦也是最稳定的一种方案。

另一种方式是利用 Frame 元标签。在主 SWF 类名上面添加

复制代码
[Frame(factoryClass=" 加载类类名 ")]

即可指定一个类作为加载类,它会在主 SWF 未加载完之前显示。这个类是一个两帧 MovieClip,当它自己加载完毕后,就可以反射出主 SWF 的内容并实例化。

可以看这篇文章: http://www.bit-101.com/blog/?p=946

而我自己的加载类模板则是这个,可以作为参考:

http://code.google.com/p/ghostcat/source/browse/trunk/GhostCat/src/ghostcat/util/load/RootLoaderBase.as

2011 年 5 月 26 日 01:393213

评论

发布
暂无评论
发现更多内容

LeetCode题解:217. 存在重复元素,哈希表,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

EMQ 映云科技成为开源项目 Vue.js 定期捐赠者

EMQ映云科技

Java 开源 前端 开源项目 emq

终于有人把TCP协议与UDP协议给搞明白了

编程菌

Java 编程 程序员 计算机 java技术宅

交易复制平台FNDZ,将以DeFi为核心打造牛市交易员

区块链小八歌

多张图片的形式

冇先生

银行小程序隐私安全如何做?诊疗一体,一步到位

WeTest

好评如潮,PerfDog两年迭代正式启动商业化探索

WeTest

使用账号密码来操作github? NO!

程序那些事

Java GitHub 程序那些事

太为难我了,阿里面试了7轮(5年经验,拿下P7岗offer)

云流

Java 编程 程序员 架构 面试

去哪儿网数据同步平台技术演进与实践

Qunar技术沙龙

数据库 数据中台 ES 数据同步 Kafk

云原生 | 混沌工程工具 ChaosBlade Operator Node 篇

RadonDB开源社区

数据库 云原生 混沌工程

基于KubeEdge实现中国移动10086客服云边协同平台

华为云原生团队

云计算 开源 运维 边缘计算 边缘技术

底层即真理!Netty+Redis+ZooKeeper解读高并发架构

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

二本渣渣5面阿里,从准备简历到“直怼”面试官,经历了什么?

云流

Java 程序员 架构 面试 计算机

给Arm生态添把火,腾讯Kona JDK Arm架构优化实践

腾源会

开源 腾讯 jdk 腾讯开源 KonaJDK

价值连城 图灵奖得主Yoshua Bengio约书亚·本吉奥的采访 给AI从业者的建议 John 易筋 ARTS 打卡 Week 60

John(易筋)

ARTS 打卡计划

千亿级模型在离线一致性保障方案详解

百度Geek说

百度 测试 后端

腾讯WeTest零售行业质量解决方案

WeTest

架构师训练营-毕业设计

俞立夫

linux工具之TC

糖米唐爹

centos8 mediasoup 搭建

糖米唐爹

WebRTC mediasoup

机会!痛点!难点!中国游戏泛娱乐企业出海攻略全解析

环信

游戏出海 直播 社交APP出海 泛娱乐社交

云时代的到来会淘汰运维人员吗?运维工作可以一直做吗?

行云管家

云计算 运维 云服务 IT运维 云时代

MySQL 系列教程之(六)DML 操作:数据的增删改

若尘

数据库 MySQL 数据库 八月日更

webrtc AlrDetector

糖米唐爹

“区块链”赋能智慧社区,多维度提升管理质效

旺链科技

区块链 智慧社区

浅谈消息中间件,MQ的来龙去脉

Java技术那些事

Java 编程 程序员 计算机 8 月日更

FNDZ构建的交易复制生态,让DeFi回归价值本身

股市老人币圈新

多线程、分布式、高并发都不懂?你拿什么跳槽?

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

业界良心啊!第五次更新的Spring Cloud Alibaba升级太多内容

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

堡垒机品牌就认行云管家!为什么呢?

行云管家

云计算 系统运维 堡垒机 IT运维 云计算运维

移动应用开发的下一站

移动应用开发的下一站

Flash务实主义(六)——Loading-InfoQ