SWF文件由一系列有顺序的“帧”构成,“帧”中包含编译后的ActionScript字节码以及所需的数据,比如图像、声音和视频。Flash Player按照“帧”的先后顺序,“试图”以固定的频率执行每一帧上的代码,以显示图形或者播放动画及语音。
Flash Player播放每一帧时,都按以下步骤进行:
1)Flash Player发出事件(Event)。这些事件包括由Timer、Mouse、ENTER_FRAMES、URLLoader等对象所发出的事件。
2)用户代码被执行。在这个阶段执行所有侦听第一步中Flash Player发出的事件的代码。这里的用户代码指的是ActionScript虚拟机以外的由开发者所编写的代码。在这里,Flex SDK也属于用户代码。
3)RENDER事件被派发。RENDER事件的派发是由用户代码中调用flash.display.Stage对象的invalidate()方法所引起的,Stage(舞台)是Flash中显示对象的根容器。invalidate()只是告知Flash Player当前的显示列表已经发生变化或者说已经失效,该方法不直接派发RENDER事件,而是由Flash Player在渲染显示列表之前检查显示列表是否失效。如果显示列表已经失效,则会在渲染新显示列表前派发RENDER事件,给用户代码最后的执行机会。
4)最后的用户代码被执行。侦听第三步RENDER事件的代码在此处被执行,这是用户代码在渲染显示列表前最后更改显示列表的机会。在此阶段,用户代码调用Stage对象的invalidate()方法则不会再派发新的RENDER事件。
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
addedToStage="onAddToStage(event);" minWidth="955" minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
private function onAddToStage(event:Event):void
{
this.stage.frameRate = 1;
this.stage.addEventListener(Event.RENDER, onRender);
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void
{
trace("enter next frame");
/**
* 如果去掉注释,就会导致RENDER事件的派发
if(this.stage)
{
this.stage.invalidate();
}
*/
}
private function onRender(event:Event):void
{
trace("before render event");
//在此处调用stage.invalidate()不会再派发RENDER事件
if(this.stage)
{
this.stage.invalidate();
}
}
]]>
</fx:Script>
<s:Button label="haha" />
</s:Application>
Flash Player渲染出最新的显示列表。渲染显示列表的过程中不会有任何用户代码被执行,这是一个黑箱操作,开发者无法参与。
Flash执行一帧的整个过程如下图所示。
Flash执行每一帧所经历的三个阶段:(User Action)用户动作阶段、(Invalidate Action)失效动作阶段、(Render Action)渲染动作阶段。
根据SWF文件规范,每个SWF文件中都设定了帧的播放频率,简称FPS(Frames Per Second)。如果应用中某帧的ActionScript代码执行量较大,消耗较多CPU时间,Flash Player会不会为了保证FPS而漏掉该帧的一些执行代码呢?答案是Flash Player 是不会漏掉帧中任何需要执行的代码和任何需要渲染的图形的。Flash Player可以看成一个弹性的跑道,执行AS代码和渲染图形是跑道上两个阶段,正常情况下,Flash Player按照SWF文件中的FPS设置去执行每一帧,当某帧中代码执行时间与渲染图形时间之和超过FPS设定的时间,那么执行一帧的时间就会超过FPS设定的时间,如同跑道发生了变形。如下图。
因此SWF文件中设定的FPS是开发者期望的最大帧播放速度,而不是帧的实际播放速度,帧的实际播放速度小于或者等于FPS。
Note: addedToStage触发时机为将显示对象添加到舞台显示列表或者将包含显示对象的子树添加至舞台显示列表,实际上调用以下方法会触发此事件:DisplayObjectContainer.addChild和DisplayObjectContainer.addChildAt(),估计spark组件的addElement和addElementAt()应该也是调用了前者。
Event.RENTER_RRAME事件,播放头进入新帧时调度,如果播放头不移动,或者只有一帧,则会继续以帧速率调度此事件。
Event.RENDER,将要更新和呈现显示列表时调度,更改的最后机会,如果希望调用render事件,必须调用Stage对象的invalidate方法