【Flash 务实主义】Loading

加载形式
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://blog.sina.com.cn/yyy98

posted @ 2012-03-22 09:43  硕气书生  Views(250)  Comments(0Edit  收藏  举报