MovieClipLoader类详解

MovieClipLoader类详解
2009年11月03日
  
本文深入研究了MovieClipLoader类的特性,并剖析了MovieClipLoader类的实现缺陷,假定读者对此类有一定的了解,并可以使用该类进行简单的诸如loading的开发,但是本文并不是一篇介绍loading的文章。
  使用moviecliploader下载过多的位图会带来计算机网络连接的拥塞,即使使用unloadClip方法取消下载,仍然不会有好转,引起这个现象的原因与这个类的实现细节有关系,我们虽然不能看到其实现的原理,但是通过它的表现,可以对其特性有所认识。
  MovieClipLoader可以胜任一般的应用,但是对于一些大量的下载任务,如果使用不当,可能带来严重的网络负担和非常差的用户体验。下面分别讨论MovieClipLoader类的各个方法。
  [b]loadClip(MovieClipLoader.loadClip 方法)[/b]
  public loadClip(url:String, target:Object) : Boolean
  下文部分摘自Flash Professional 8 官方文档。
  QUOTE:
  在播放原始影片时,将 SWF、JPEG、渐进式 JPEG、非动画 GIF 或 PNG 文件加载到 Flash Player 中的影片剪辑中。如果您加载 GIF 动画,仅显示第一帧。使用此方法可以一次显示多个 SWF 文件,并且无需加载另一个 HTML 文档即可在 SWF 文件间进行切换。
  使用 loadClip() 方法代替 loadMovie() 或 MovieClip.loadMovie() 具有许多优点。通过使用侦听器对象来实现下列处理函数。通过使用 MovieClipLoader.addListener(listenerObject) 向 MovieClipLoader 类注册侦听器,可以激活该侦听器。
  在加载开始时调用 MovieClipLoader.onLoadStart 处理函数。
  在无法加载剪辑时调用 MovieClipLoader.onLoadError 处理函数。
  在加载进程正进行时调用 MovieClipLoader.onLoadProgress 处理函数。
  在文件完成下载但已加载的影片剪辑的方法和属性尚不可用时调用 MovieClipLoader.onLoadComplete 处理函数。在 onLoadInit 处理函数之前调用此处理函数。
  在执行该剪辑的第一帧中的动作后调用 MovieClipLoader.onLoadInit 处理函数,以便您可以开始处理加载的剪辑。在 onLoadComplete 处理函数之后调用此处理函数。在大多数情况下,请使用 onLoadInit 处理函数。
  加载到影片剪辑的 SWF 文件或图像会继承该影片剪辑的位置、旋转和缩放属性。可以用该影片剪辑的目标路径来定位加载的影片。
  您可以使用 loadClip() 方法将一个或多个文件加载到单个影片剪辑或级别中;将 MovieClipLoader 侦听器对象作为参数传递给正加载的目标影片剪辑实例。或者,您可以为加载的每个文件创建不同的 MovieClipLoader 对象。
  使用 MovieClipLoader.unloadClip() 可删除用此方法加载的影片或图像,或者取消正在进行中的加载操作。
  首先需要指出的是,loadClip方法对于Flash编程人员来说是多线程的,不管其内部实现机制如何,一个事实可以证明这一点:我们可以使用 loadClip方法同时下载多个图片,并把不同的图片放在不同或者相同的电影剪辑当中。(文档中仅仅指出可以放在同一个剪辑当中)。
  第二点,MovieClipLoader.onLoadStart处理函数并不是调用loadClip后会立即触发。文档称,当被加载的剪辑或者图片的第一个字节被写入用户磁盘中时,此函数被调用。可以确定,当网络连接不可用或者被下载资源不可用的时候就可能用原不会触发此事件。
  [b]unloadClip(MovieClipLoader.unloadClip 方法)[/b]
  public unloadClip(target:Object) : Boolean
  下文红色部分摘自Flash Professional 8 官方文档。
  QUOTE:
  删除通过使用 MovieClipLoader.loadClip() 加载的影片剪辑。如果您在正加载影片时发出此命令,则调用 MovieClipLoader.onLoadError。
  可用性:ActionScript 1.0;Flash Player 7
  参数
  target:Object - 传递至对 my_mcl.loadClip() 的相应调用的字符串或整数。
  返回
  Boolean - 一个布尔值。如果删除影片剪辑成功,则返回 true;否则返回 false。
  示例
  下面的示例将图像加载到名为 image_mc 的影片剪辑中。如果单击影片剪辑,则会删除该影片剪辑,并且信息会显示在"输出"面板中。
  this.createEmptyMovieClip("image_mc", this.getNextHighestDepth());
  var mclListener:Object = new Object();
  mclListener.onLoadInit = function(target_mc:MovieClip) {
  target_mc._x = 100;
  target_mc._y = 100;
  target_mc.onRelease = function() {
  trace("Unloading clip...");
  trace("\t name: "+target_mc._name);
  trace("\t url: "+target_mc._ur ;
  image_mcl.unloadClip(target_mc);
  };
  };
  var image_mcl:MovieClipLoader = new MovieClipLoader();
  image_mcl.addListener(mclListener);
  image_mcl.loadClip("http://www.helpexamples.com/flash/images/image1.jpg", image_mc);
  此方法是我们讨论的核心所在。loadClip文档称:使用 MovieClipLoader.unloadClip() 可删除用此方法加载的影片或图像,或者取消正在进行中的加载操作。我们知道,loadClip方法是需要占用网络连接核内存资源的,我们寄希望于一旦调用 unloadclip则立即释放网络连接和内存资源。但是事与愿违!这是MovieClipLoader方法的关键问题。当AS调用 MovieClipLoader.unloadClip()之后,并不一定会马上释放资源。当我们使用MovieClipLoader下载大量的图片的时候,虽然我们在调用unloadClip之后才下载新的图片,但是网络连接的使用将进一步累积增大,导致网络连接的暂时阻塞。
  经过测试,调用loadClip方法之后,立即调用unloadClip方法是丝毫不起作用的,下载过程会继续进行,并且MovieClipLoader类的事件处理函数仍然会被调用。这看起来非常出乎人的意料之外!而且非常不合理,但是事实就是如此。另外,如果手动将被加载对象的目的剪辑删除(unloadMovie或者removeMovieClip),这将删除舞台上的剪辑,但是仍然不能释放MovieClipLoader所占用的资源。
  事实证明,当MovieClipLoader.onLoadStart被调用之后(注意:这是loadClip开始之后第一个可能被触发的事件),再次使用 unloadClip方法,将会删除被加载的剪辑,但是同时网络连接也会被释放。但是这样做的并没有太大的意义,因为从网络占用开始到下载第一个字节,期间的资源消耗是不可消除的,但这卡恰又是关键的资源。这段无意义的资源占用会字节导致用户计算机的网络阻塞,表现为上网速度突然降低,几秒钟之后恢复正常(这要看用户的网络速度如何以及同时下载的图片数有多大)。
  这里判断网络连接被释放的方法有点特别。因为当Flash Professional 8在测试影片时,如果Flash正在使用网络连接来下载数据,这是关闭Flash测试窗口会导致Flash 8 Professional开发环境的异常。这个异常并不会给用户以任何提示,但是此后的影片测试动作将无效,不能开启影片测试窗口,甚至可能导致 Flash崩溃。
  当onLoadStart事件触发之后,使用unloadClip,并且立即关闭测试窗口,不会带来上述的开发环境异常的问题,因此我猜测网络连接已经释放了。当然,肯定是有更好的方式去判断是否释放了网络连接。
  unloadClip 的存在的另外一个问题是MovieClipLoader.onLoadError事件的触发问题。文档指出:如果您在正加载影片时发出此命令,则调用 MovieClipLoader.onLoadError。但是通过我的测试,不论何时调用了unloadClip方法, MovieClipLoader.onLoadError都不会被触发!这是另一个非常惊人的现象!
  [b]现在我还没有找到一个方法,可以彻底的从内存中删除一个对象。我们知道,as是使用垃圾收集机制来管理内存的,我们并不能直接调用垃圾收集动作,也就是说,大多数时候,我们设置一个对象的唯一引用为null,那么可以判断这个对象已经符合垃圾收集的条件,但是这个对象并不会立即被破坏,它所占用的内存和其他资源并不会立即被释放。因此,我们没有办法在所有时候立即释放MovieClipLoader所占用的资源。[/b]
  [b]onLoadInit(MovieClipLoader.onLoadInit 事件侦听器)[/b]onLoadInit = function([target_mc:MovieClip]) {}
  下文红色部分摘自Flash Professional 8 官方文档。
  QUOTE:
  当执行加载的剪辑的第一帧上的动作时调用。在调用此侦听器后,您可以设置属性、使用方法,还可以与加载的影片交互。对通过使用 MovieClipLoader.addListener() 添加的侦听器对象调用此侦听器。
  target_mc 的值标识作为这一调用的目标的影片剪辑。此参数在使用同一组侦听器加载多个文件时非常有用。
  这里需要补充一点:onLoadInit是在调用被加载剪辑的第一帧的代码之后被触发。onLoadCompelete触发是在被加载对象的最后一个字节被写入用户磁盘的时候被调用,在此之后和onLoadInit之前,被加载对象的内部数据是不可用的。然而,这里的内部数据指的是被加载对象的帧代码以及其子剪辑,被加载对象本身的属性和方法是有效的!也就是说,将图片加载到mc中的过程中的任何时候,mc._x是始终可以被使用的。
  [b]最终结果:今天我使用sniffer观察IP数据包之后,发现多个MovieclipLoader会共用TCP:http网络连接,只要前一个任务下载完毕,就可以释放连接给下一个任务使用,这是MovieclipLoader实现中非常明智的地方。但是如果尚没有任务下载完毕,也就是没有空闲TCP: http网络连接,就会创建新连接。
  因此如果不断下载新图片并且在旧尚未下载完之前移除target_mc,由于 MovieClipLoader.unloadClip、MovieClip.removeMovieClip以及 MovieClip.unloadClip都只能仅仅删除场景尚的图片,而不能立即释放TCP:http网络连接,这样导致每添加一个新任务都会创建新连接,这将大大耗费资源,最终导致网络阻塞!!!
  至此MovieclipLoader的关键问题就很明显了:MovieclipLoader类没有提供立即释放网络连接的方法,这是问题的根源。[/b]
  注:本文涉及内容可能会有争议,如有疏漏或者错误,敬请指正。
  附录:本文用户讨论的实例的代码如下,由于个人站空间有限,不能提供源文件下载。主场景中有两个按钮,实例名为start和cancel。为了使trace有效,请在开发环境中测试这些代码,而不是发布之后运行。
  //http://www.cloudward.net/map/flash_51ditu.swf
  var mcl=new MovieClipLoader();
  var started;
  var mc:MovieClip;
  start.onRelease=function(){
  started=true;
  mcm=_root.createEmptyMovieClip("mcm",10);
  mc=mcm.createEmptyMovieClip("mc",11);
  mcl.loadClip("http://www.cloudward.net/map/flash_51ditu.swf",mc);
  }
  cancel.onRelease=function(){
  trace(_root.mcm);
  _root.mcm.removeMovieClip();
  //unloadMovie(_root.mc);
  //mcl.unloadClip(mc);
  }
  var listener:Object=new Object();
  listener.onLoadComplete=function(){
  trace("onLoadComplete");
  }
  listener.onLoadError=function(){
  trace("onLoadError");
  }
  listener.onLoadStart=function(mc){
  //mc.unloadMovie();
  trace("onLoadStart");
  mcl.unloadClip(mc);
  }
  this.onEnterFrame=function(){
  var o:Object=mcl.getProgress(mc);
  //if(started && o.bytesLoaded!=o.bytesTotal)
  //trace(Math.round(o.bytesLoaded/o.bytesTotal*100));
  }
  mcl.addListener(listener);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值