使用moviecliploader下载过多的位图会带来计算机网络连接的拥塞,即使使用unloadClip方法取消下载,仍然不会有好转,引起这个现象的原因与这个类的实现细节有关系,我们虽然不能看到其实现的原理,但是通过它的表现,可以对其特性有所认识。
MovieClipLoader可以胜任一般的应用,但是对于一些大量的下载任务,如果使用不当,可能带来严重的网络负担和非常差的用户体验。
public loadClip(url:String, target:Object) : Boolean
首先需要指出的是,loadClip方法对于Flash编程人员来说是多线程的,不管其内部实现机制如何,一个事实可以证明这一点:我们可以使用loadClip方法同时下载多个图片,并把不同的图片放在不同或者相同的电影剪辑当中。(文档中仅仅指出可以放在同一个剪辑当中)。
第二点,MovieClipLoader.onLoadStart处理函数并不是调用loadClip后会立即触发。文档称,当被加载的剪辑或者图片的第一个字节被写入用户磁盘中时,此函数被调用。可以确定,当网络连接不可用或者被下载资源不可用的时候就可能用原不会触发此事件。
public unloadClip(target:Object) : Boolean
此方法是我们讨论的核心所在。官方loadClip文档称:“使用 MovieClipLoader.unloadClip() 可删除用此方法加载的影片或图像,或者取消正在进行中的加载操作。”我们知道,loadClip方法是需要占用网络连接核内存资源的,我们寄希望于一旦调用unloadclip则立即释放网络连接和内存资源。但是事与愿违!这是MovieClipLoader方法的关键问题。当AS调用MovieClipLoader.unloadClip()之后,并不一定会马上释放资源。当我们使用MovieClipLoader下载大量的图片的时候,虽然我们在调用unloadClip之后才下载新的图片,但是网络连接的使用将进一步累积增大,导致网络连接的暂时阻塞。
经过测试,调用loadClip方法之后,立即调用unloadClip方法是丝毫不起作用的,下载过程会继续进行,并且MovieClipLoader类的事件处理函数仍然会被调用。这看起来非常出乎人的意料之外!而且非常不合理,但是事实就是如此。另外,如果手动将被加载对象的目的剪辑删除(unloadMovie或者removeMovieClip),这将删除舞台上的剪辑,但是仍然不能释放MovieClipLoader所占用的资源。
事实证明,当MovieClipLoader.onLoadComplete被调用之后,再次使用unloadClip方法,将会删除被加载的剪辑,但是同时网络连接的占用也会被释放。但是这样做的并没有太大的意义,因为图片一旦开始下载,资源消耗是不可消除的,但这恰又是关键的资源。这段无意义的资源占用会字节导致用户计算机的网络阻塞,表现为上网速度突然降低,几秒钟之后恢复正常(这要看用户的网络速度如何以及同时下载的图片数有多大)。
unloadClip的存在的另外一个问题是MovieClipLoader.onLoadError事件的触发问题。官方文档指出:“如果您在正加载影片时发出此命令,则调用 MovieClipLoader.onLoadError。”但是通过我的测试,不论何时调用了unloadClip方法,MovieClipLoader.onLoadError都不会被触发!这是另一个非常惊人的现象!
现在我还没有找到一个方法,可以彻底的从内存中删除一个对象。我们知道,as是使用垃圾收集机制来管理内存的,我们并不能直接调用垃圾收集动作,也就是说,大多数时候,我们设置一个对象的唯一引用为null,那么可以判断这个对象已经符合垃圾收集的条件,但是这个对象并不会立即被破坏,它所占用的内存和其他资源并不会立即被释放。因此,我们没有办法在所有时候立即释放MovieClipLoader所占用的资源。
onLoadInit = function([target_mc:MovieClip]) {}
这里需要补充一点:onLoadInit是在调用被加载剪辑的第一帧的代码之后被触发。onLoadCompelete触发是在被加载对象的最后一个字节被写入用户磁盘的时候被调用,在此之后和onLoadInit之前,被加载对象的内部数据是不可用的。然而,这里的内部数据指的是被加载对象的帧代码以及其子剪辑,被加载对象本身的属性和方法是有效的!也就是说,将图片加载到mc中的过程中的任何时候,mc._x是始终可以被使用的。
最终结局
我使用sniffer观察IP数据包之后,发现多个MovieclipLoader会共用TCP:http网络连接,只要前一个任务下载完毕,就可以释放连接给下一个任务使用,这是MovieclipLoader实现中非常明智的地方。但是如果尚没有任务下载完毕,也就是没有空闲TCP:http网络连接,就会创建新连接,旧链接在任务完成或者其他很短的时间内被作废,通过观察发现,无论多少个MovieClipLoader同时下载多少个任务,并发使用中的网络连接数最多是10,但是已经开启却不再使用了的网络链接这可能很多。如果不断下载新图片并且在旧尚未下载完之前移除target_mc,由于无法立即释放这些被删除的图片所占用的TCP:http网络连接,那么会导致每添加一个新下载任务都会创建新连接,旧链接被作废。这样一来,无用的网络链接占用就会很多,导致网络阻塞。
重用网络连接的唯一方法:永远不要在onLoadCompelete完成之前删除一个被加载的图片;决定要下载一个新图片时要确定是否真的必须下载。
至此MovieclipLoader的关键问题就很明显了:MovieclipLoader类没有提供立即释放网络连接的方法,这是问题的根源。