现在的位置: 首页 > 综合 > 正文

谈Flex组件的生命周期

2018年01月26日 ⁄ 综合 ⁄ 共 5733字 ⁄ 字号 评论关闭

引子:作于一名由ASP.NET开发转到Flex开发的程序员,最开始适应的过程是相当愉悦和轻松的。由于我早在ASP.NET中习惯了标签式的布局和对应的code behind的编程方式,在Flex中的MXML语言只不过是MXML元素和AS3语法的重新熟悉。但是就像ASP.NET中的标记语言,MXML只不过是AS3的一种封装,归根到底它会被转化成AS3再进行编译,所以,只有了解这门语言的底层运行机制,才有可能真正成为一名优秀的Flex开发人员。由于有过ASP.NET的开发经验,我清楚地认识到像这种以XML标签为基础的组件式的开发方式,只有弄清楚组件的生命周期,才能进行高级开发,才能在遇到复杂情况时有思路去解决,就像做ASP.NET的开发要熟悉页面的生命周期一样。

为此,我刚接触Flex不久就开始研究Flex组件的生命周期。幸运的是,Flex Builder内嵌的帮助文件十分犀利,很容易就查到了Flex组件的的生命周期部分。不幸的是作为没有Flash开发经验的程序员,对里面的某些名词并不是很明白。就这样在没有Flash基础的情况下,我曾经看过很多遍帮助文件,但感觉脑子里一直不是很清楚,对整个生命周期不能形成一个完整的轮廓。最后终于滤清了一些思路,结果还是停在了一个名词上,叫做“Render Event”。里面两次提到了这个词,都是在关键的地方。前些天搞到了《Essential.ActionScript.3.0》的电子版,阅之如醍醐灌顶,不愧为AS3的经典。其中一章就专门讲了Flash运行时的屏幕更新机制,里面当然也提到了Flex帮助文件中所谓的“Render
Event”。

先看看Flex帮助文件是怎么说的吧,这里拷贝了原文(可以先略过,只为提供参考):

 

  1. You call the component’s constructor, as the following code shows:
    // Create a Button control.
    var b:Button = new Button()
  2. You configure the component by setting its properties, as the following code shows:
    // Configure the button control.
    b.label = "Submit";

    Component setter methods might call the invalidateProperties(),
    invalidateSize()
    , or invalidateDisplayList() methods.

  3. You call the addChild() method to add the component to its parent, as the following code shows:
    // Add the Button control to the Box container.
    boxContainer.addChild(b);

    Flex performs the following actions:

  4. Sets the parent property for the component to reference its parent container.
  5. Computes the style settings for the component.
  6. Dispatches the preinitialize event on the component.
  7. Calls the component’s createChildren() method.
  8. Calls the invalidateProperties(), invalidateSize(), and
    invalidateDisplayList() methods to trigger later calls to the
    commitProperties()
    , measure(), or updateDisplayList() methods during the next
    render event.The only exception to this rule is that Flex does not call the
    measure() method when the user sets the height and width of the component.
  9. Dispatches the initialize event on the component. At this time, all of the component’s children are initialized, but the component has not been sized or processed for layout. You can use this event to perform additional processing of the component
    before it is laid out.
  10. Dispatches the childAdd event on the parent container.
  11. Dispatches the initialize event on the parent container.
  12. During the next render event, Flex performs the following actions:
    1. Calls the component’s commitProperties() method.
    2. Calls the component’s measure() method.
    3. Calls the component’s layoutChrome() method.
    4. Calls the component’s updateDisplayList() method.
    5. Dispatches the updateComplete event on the component.
  13. Flex dispatches additional render events if the commitProperties(),
    measure(), or updateDisplayList() methods call the
    invalidateProperties()
    , invalidateSize(), or invalidateDisplayList() methods.
  14. After the last render event occurs, Flex performs the following actions:
    1. Makes the component visible by setting the visible property to
      true
      .
    2. Dispatches the creationComplete event on the component. The component is sized and processed for layout. This event is only dispatched once when the component is created.
  15. Dispatches the updateComplete event on the component. Flex dispatches additional
    updateComplete events whenever the layout, position, size, or other visual characteristic of the component changes and the component is updated for display.

看起来这么多步,总结起来其实就三步:

1. 实例化阶段(1-6)。这个阶段调用组件的构造函数,设置组件的属性和样式。
2. 初始化阶段,说白了就是构造显示列表阶段(7-11)。在这个阶段,以这个组件为根节点,在数据结构层面构造显示列表(相对于屏幕呈现层面)。
3. 屏幕呈现阶段(12-14)。在这个阶段,其实就是前面提到的Render Event抛出后(关于Render Event请看下面的分析),执行那些布局的方法,包括设置组件大小,设置位置等等。最后将组件的visible属性设为true,呈现组件,然后就会抛出creationComplete事件。
  1. 实例化阶段(1-6)。这个阶段调用组件的构造函数,设置组件的属性和样式。
  2. 初始化阶段,说白了就是构造显示列表阶段(7-11)。在这个阶段,以这个组件为根节点,在数据结构层面构造显示列表(相对于屏幕呈现层面)。
  3. 屏幕呈现阶段(12-14)。在这个阶段,其实就是前面提到的Render Event抛出后(关于Render Event请看下面的分析),执行那些布局的方法,包括设置组件大小,设置位置等等。最后将组件的visible属性设为true,呈现组件,然后就会抛出creationComplete事件。

你也可以从按事件来分阶段,结果是一样的。preinitialize事件标志着实例化阶段的结束;initialize事件则标志着构造显示列表阶段的结束;creationComplete 事件当然就是标着着屏幕呈现阶段的结束了。起始这些阶段这么分就是为了容易理解,并不是死的。

现在到了关键部分了,里面的“Render Event”是个啥意思?其实就像视频一样,flash中有帧这样的概念。不同的视频文件可以有不同的帧率当然就是,flash也可以自己控制帧率。Flex默认一秒24帧,而高速摄像机能达到1000000帧/秒。flash开始就是做动画的,后来模型奇妙的成为RIA界的扛把子,所以到现在Flex框架,还是得继承那套以动画为基础的衣钵。就像Java,本来设计是用来做机顶盒的软件的,结果不小心成了目前最流行的开发语言,这就是命。当然这都是题外话了。回到我们的“Render Event”,一切还要从Flash中的屏幕更新机制说起。

Flash运行时中有两种屏幕更新机制

一种是定时更新(Scheduled Screen Updates),这就跟帧率相关了。比如说Flex默认的一秒24帧,那Flash运行时就会每隔1/24秒检查一下程序状态,看看有没有添加组件啊,有没有组件被重新设置导致大小,颜色有变化啊等等,如果有,就重绘屏幕,以将变化呈现到屏幕上。当然这里的1/24秒是理想的频率,实际过程往往由于其他代码执行导致相应的拖延,但如果没有代码执行并不会提前,而是等着,一直到过完这1/24秒。所以说所谓的帧率只能体现一个最快帧率,实际帧率要有实际情况决定。

还有一种机制是事件强制更新(Post-Event Screen Updates)。这种机制是专门为某些事件服务的,如鼠标事件和键盘事件。由于鼠标事件需要一个即时的反应,就需要事件派发后第一时间更新屏幕,这样就可以调用MouseEvent或者KeyboardEvent的一个实例方法updateAfterEvent(),这样就可以在事件派发后所有事件处理函数执行完后更新屏幕,不用等待。

关于Render Event

请允许我将《Essential.ActionScript.3.0》中对Render Event的描述直接粘贴在这里,讲的实在是太精彩了。我唯恐我的理解或转译有问题。

The Event.RENDER event is a specialized type of screen-update event used in advanced situations where graphics performance is critical. Its primary purpose is to let the programmer defer the execution of all custom drawing routines until the precise moment
before the screen is rendered, thus eliminating duplicate drawing-routine execution. Unlike all other built-in Flash runtime events, the Event.RENDER event must be requested manually by the programmer. The Flash runtime dispatches the Event.RENDER event when
the following two conditions are both true:

  • The Flash runtime is about to check if the screen needs updating (whether due to a frame passing or an updateAfterEvent( ) call).
  • The programmer has invoked stage.invalidate( ). (stage.invalidate( ) is the programmer’s way of asking the Flash runtime to dispatch the Event.RENDER event the next time a screen-update check occurs).

Colin Moock已经讲的很清楚了,Flash运行时将要检查屏幕是否需要更新前,会先检查程序员时候调用过stage.invalidate( )这个方法,只有调用过,才会抛出这个Event.RENDER事件。这样做的目的就是允许程序员延迟那些引起屏幕更新的操作,让它们在真正屏幕更新之前(Event.RENDER事件之后),只执行一次就够了。stage.invalidate()这个方法是不是有些眼熟,如果没猜错的话,UIComponent里的invalidateProperties(), invalidateSize(),
and invalidateDisplayList() 就会间接地调用stage.invalidate()方法,从而导致在下一个Event.RENDER的时候会调用commitProperties(), measure(), or updateDisplayList() 这些方法。

结语

如果你曾经有和我类似的困惑,我想讲到这里应该已经明白了。Colin Moock在《Essential.ActionScript.3.0》这本书中甚至模拟了Flex组件的屏幕更新机制,就在前面那段文字的后面。大师不愧为大师,写的东西确实让人受益匪浅。再有困惑的话就去找这本书吧。

抱歉!评论已关闭.