Flash Timer无故停止之谜

项目底层资源Cache超时使用了Timer驱动,每个资源到期Cache超时时会进行“释放”。后来发现有明显内存泄漏,排查出是因为Timer未进行导致。Oh,卖糕的~

ResourceManager为单例,Timer在工程启动时开始执行,为了方便说明见如下示例:

    public class Demo extends Sprite{
        private var _t:Timer;

        public function Demo(){
            _t = new Timer(500);
            _t.addEventListener(TimerEvent.TIMER, onTimer);
            _t.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete);
            _t.start();
            trace("is running:" + _t.running);
        }

        private function onTimer(e:Event):void
        {
            trace("timer");
        }

        private function onComplete(e:Event):void
        {
            trace("complete");
        }
    }

输出:
is running:true

以上结果应该在意料之中?

但,只打印了这一行,onTimer()和onComplete()中的trace()未执行,问题就出在这。正常情况应该至少会每500ms打印一次“timer”。

若增加一个ENTER_FRAME监听,每帧输出Timer的状态,如下:

        public function Demo(){
            _t = new Timer(500);
            _t.addEventListener(TimerEvent.TIMER, onTimer);
            _t.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete);
            _t.start();
            trace("is running:" + _t.running);

            addEventListener(Event.ENTER_FRAME, onEnterFame);
        }

        private function onEnterFame(e:Event):void
        {
            trace("is running:" + _t.running);
        }

        private function onTimer():void
        {
            trace("timer");
        }

        private function onComplete(e:Event):void
        {
            trace("complete");
        }

输出:
is running:true
is running:false
is running:false
……

我们发现,从第二帧开始Timer已经停止,但TIMER、TIMER_COMPLETE事件监听未触发。

Timer莫名其妙地挂了有木有!这么基础的逻辑变得不可靠有木有!!有多少功能是依靠Timer驱动的有木有!!!再也不相信爱情了有木有!!!……

以上就是问题和现象的描述,首先我们不能轻易怀疑Flash SDK有Bug,由于问题不是100%重现,苦B地排查着。

直到有一天另一个问题出现,工程启动里需要切换播放几组动画,切换时机由一Timer驱动,问题表现为动画未切换且100%重现。代码就不帖了,断点结果与前一问一模一样,Timer启动后的第二帧莫名停止且不抛任何事件。

解决

请观察第一组代码Demo.as为工程入口,Timer的初始化和启动是在Demo的构造方法中进行,貌似有问题是不?

Flash在启动时几个时间结点的执行先后:

  1. 执行Demo(主入口类)构造方法
  2. 抛出Demo.swf加载器loaderInfo的INIT事件
  3. 抛出Demo被添加到Stage的事件ADDED_TO_STAGE
  4. 抛出loaderInfo的COMPLETE事件

以下摘自DOC

INIT:当已加载的 SWF 文件的属性和方法可供访问并做好使用准备时进行分派。不过,内容可能仍在下载之中。如果存在以下条件,则 LoaderInfo 对象将分派 init 事件:

  • 可以访问与加载的对象关联的所有属性和方法以及与 LoaderInfo 对象关联的属性和方法。
  • 所有子对象的构造函数已完成。
  • 已执行所加载的 SWF 主时间轴的第一帧中的所有 ActionScript 代码。

COMPLETE:成功加载数据后分派。也就是说,当已下载所有内容并且已完成加载过程时分派它。complete 事件始终在 init 事件之后分派。当该对象已做好访问准备(尽管可能仍在下载内容)时,将分派 init 事件。

问题出在,我们在构造方法中执行的逻辑,当工程还未完全初始化或未加载完成时启动了Timer,当工程准备就绪时Timer将被重置。且不会触发任何事件。

将代码改为如下,在COMPLETE后进行Timer初始化,可解决问题

    public class Demo extends Sprite{
        private var _t:Timer;

        public function Demo(){
            loaderInfo.addEventListener(Event.COMPLETE, onAppReady);
        }

        private function onAppReady(e:Event):void
        {
            _t = new Timer(500);
            _t.start();
        }
    }

标签:flash, timer

添加新评论