理解Flex3的组件和框架的生命周期(一)AdobeFlex框架的SDK美中不足之处就是部分由良好的半黑盒子系统所创建;这就是说,在大多数情况下,我们这些开发者在项目周期中没有时间或精力去真正地深入到未知领域。从技术上来讲,Flex框架的不是一个黑盒子,你可以阅读和查看它的所有源码。由于代码的复杂性及它是如何设计的,我们往往把框架看成是如何输入、得到什么样的输出。大多数开发人员,包括作者,往往在工作中学习Flex,通过验证、试验、研究或按照他人写的博客提到的观点来寻找新的技巧和技术。Adobe已经做了非常了不起的工作是Flex框架的文档,它已被细分为两大类:用户指南和API(ASDoc)文档。然而,即使他们有大量的文档,用户指南和API文档之间的还是有一个很大的差距。用户向导涵盖了一系列的课题,从如何开始到使用比较复杂的功能;但都停止在这些不很复杂的功能上,比如:样式扩展的技巧,元数据可以做和不可以做的,当然也包括Flex框架和组件生命周期。有很高层次的概述,但这些都在用户能弄明白的能力范围之外。从API文档中我们可以推断出很多功能和命令,有时它们是引导我们,但也有时关于一些信息会戏弄我们。通常,API文档假定你知道要查找的东西,它们只是解释一下如何去做;但不会告诉你何时或为什么去用它们。由于这种宏观的指导与微观的API之间的差距,优化开发成为了一个黑色艺术,需要程序员通过多年的经验在试验和错误中确定最佳的做法。加上这些与Flex只有5年左右的历史,它从最初的表现进化到了引人注目的阶段,而我们大多处于技术的初级阶段。然而,作为第三方的开发者,并不是唯一留在外面的。多数Adobe的工程师也没有完全了解框架的细微部分。詹姆斯参加了Adobe2008年MAX会议关于Flex组件的介绍和Deepa论坛关于Flex的生命周期评价。她的报告一半是关于生命周期,甚至其中一半是关于在当前Flex3的生命周期,其余的是关于Flex4的变化。随着她在高层次的深入概述,提到近期一个Flex架构师使她最终通过了整个生命周期。她对观众说希望能早点了解这些信息,这样能使她能成为一个更好的工程师。我们的观点是Flex迷惑了我们许多人,甚至一些最受尊敬的工程师。这是一个复杂的系统设计得非常强大,易扩展,灵活。然而,这种灵活性推展和实现创造了许多可能性。这些可能性很多都不是最好的开发方式,甚至有可能不利于整体性能,稳定性和可扩展性的应用前景。本文的目的是尝试照耀到整个生命周期,使我们一些启示,作为一个开发社区,用Flex能创造更好的应用程序和组件。值得提醒的是,以下信息大部分是从阅读源代码推断的,一些是好的文档,也有一些不是。如果你看到一些不太正确的,或许可以用一个更好的解决方式,欢迎随时联络DevelopmentArc我们(info@developmentarc.com),以便我们尽可能添加/更新此文档,使之在技术上正确的。如何阅读在整本书中我们所涉及许多的Flex框架源码,但为了简洁,我们不总是显示所指的代码。当你阅读这本书时,要求你打开FlexBuilder,或能够访问Flex3框架的源码,跟随着我们所讨论源码是怎么工作及为什么这样做。如果你跟着阅读源码,请注意,我们经常跳过功能或者具体的代码,以便我们可以对应当前的主题。这样能防止我们远离当前的主题,主要是讲解代码的微妙之处。这并不是说那些代码的作用不重要,而是那些代码处理特别的案例,防止潜在的错误或在生命周期的后面来处理,只是我们当前没有讨论它。Flex简史在我们进入Flex框架细枝末节及如何利用它的讨论之前,我们应该退一步,看看整体的Flex大局,为什么我们要使用它。许多读者也许已有相关的Flex经验,也许你有很多的开发经验;但是为了了解Flex小组的决定,我们有必要去看一看Flex和FlashPlayer的历史。这一点很重要,因为Flex框架的许多部分对FlashPlayer相当较低。都是关于帧从根本上讲,Flex就是Flash.在DevelopemntArc,当我们坐下教授初级开发者或客户时,关于Flex我们都会继续重复这一点。因为Flex最终生成的SWF文件和AdobeFlashProfessional工具开发生成的一样。这就意味着,Flex和任何FlashSWF都是一样地遵守相同的规定。一个最根本的规则就是所有的SWF文件都要转换成以Flash为基础框架。对于那些不熟悉Flash历史的人来说,玩家的最初目标就是创建动画。传统的动画是以细胞为基础,你画的图像在某个细胞被稍微修改的图像的细胞所替换,它在一定的速度下,看起来像有运动的变化。所以,Flash使用frame创建相同的动画效果来代替细胞。帧依然存在,所有的逻辑和屏幕的渲染依赖于帧改变和帧率定义的大小。帧率告诉Flash每秒多少帧应该尝试处理;Flex的默认设置为每秒24帧。这并不能保证Flex每秒都是24帧,只保证做的帧数不会超过24.实际的帧率受很多因素的影响,比如:复杂的代码被执行后,造成数量的改变需要重新呈现在屏幕上;另外,当然包括机器处理FlashPlayer的表现能力。可变跑道TedPatrick写了一篇关于Flash帧执行顺序的优秀文章并将它命名为可变跑道。关于FlashPlayer7的旧文章,从理论上讲对于今天的FlashPlayer10仍然成立。他的文章主要讲的是将Flash帧分为两个阶段:代码执行和屏幕重绘/渲染阶段。代码执行是线性的,在当前帧要求所有的代码都执行完,并没有其它线程。在这个过程中,产生数据,计算数值和修改显示的列表。一旦所有的代码执行完,显示列表就会更新,接着FlashPlayer把改变的部分渲染到屏幕上。随着FlashPlayer9和ActionScript3的发布,开发组还创建了新的ActionScript虚拟机(AVM2)支持新的语言及特点。他们还重新设计了FlashDOM使新的Player能支持早期的开发。SeanChristmann,运用TedPatrick的可变跑道概念将EffectiveUI进行了更新,并在新的虚拟机中显示出来。Sean给跑道添加了“TheMarshal”这个概念。老的虚拟机(AVM1)和AVM2最主要的不同是AVM1只工作在一片帧的环境下。AVM2的Marshal主要负责为FlashPlayer分割时间片去执行;最重要的在前面声明这些时间片,与将帧率编译在一个swf文件中并不一回事;播放器最终从这些时间片合成一个帧率。这些时间片被分为多个步骤来完成:播放器事件的派发,用户代码的执行,预渲染事件的派发,用户代码的代码和最终播放器的渲染。Sean研究了这些步骤,什么时候执行和当被执行时帧率是如何被修改的。我们强烈建议阅读本书前先查阅这两篇文章,因为我们的讨论将一直涉及到帧,及Flex框架在整个系统的重要性。管理跑道执行代码和渲染显示内容需要花费很多的时间去完成,代码越复杂或改变显示的内容越多,则渲染花费的时间更久。其中最常见令开发者比较头痛的是可变跑道在处理大数据量时,动画的表现总是滞后。例如,在DevelopmentArc的一个客户端工程中,用FlexTween系统创建了一个下载的动画。单独运行它时看上去非常好,一旦我们去向服务端下载数据时,动画就变得非常波动。导致这处现象的原因是我们要求播放器向服务端请求一个大量的XML数据,同时试图以编程方式让动画在屏幕上进行移动。我们已做的是超负荷的跑道(帧播放器代码方面的),播放器在每个周期花费太多时间去计算,所以导致没有足够的时间去渲染。这就是说播放器的帧率没有完全满足动画显示的需求,因大多时间被计算所占去。Ted's在跑道这篇文章的最后评语是:正如前面说过很多次,‘只是等待一帧’。这引导我们回到Flex框架上来。由于Flex框架的复杂性,想象它将如何执行,如果要在一个单一的每一帧处理嵌套的布局计算(HBox,VBox),动画(特效,过渡),数据加载(远程对象,HTTP服务),用户交互(鼠标,键盘),所有的定制代码。这将使播放器屈服和Flex完全无法使用。框架大部分为我们所做的就是‘等待一帧’。它的架构设计就是以这种方式来创建阶段和步骤进行优化播放器该如何工作,你可以利用这一优点,采用和开发API钩子。理解Flex3的组件和框架的生命周期(二)Flex应用程序的生命阶段所有的生物生命周期我们都分为:出生,生长,成熟,死亡。正如生物的生命周期,Flex框架和它的组件也有相似的过程。我们将检验出生过程,看组件是如何被创建的,如何生长及成熟,到最后将从系统中它们移除时即为生命的终止。在查看完组件后,我们将查看Flex应用程序框架的生命周期。我们将重新查看Flex框架是如何开始,及与FlashPlayer结合,创建它们的程序和它们的子组件。我们将会关注程序在生长阶段会发生什么现象及在成熟阶段当与用户交互时会发生什么。最后我们看一下应用程序的销毁阶段。理解这些所有的因素和它们发生的顺序将会帮助你设计和开发出更好的组件和应用程序。一旦你完全了解它们的生命过程,利用它们的优点来为你的程序添加新的功能。组件生命周期的介绍在讨论Flex应用程序如何开始和创建它们的孩子之前,我们就先花点时间看一看一般组件的生命周期。首先理解组件的生命周期是很重要的,因为Flex应用程序的生命周期是组件生命周期的扩展,再加上一些重要的修改来帮助性能和其它所需要的系统功能。组件阶段:概述一个Flex组件都经过七个不同的阶段,我们可以划分到4个生命阶段。由于组件整个生命周期主要有七个阶段,一些阶段是重复的出现,有的只出现一次。这七个阶段是构建,添加,初始化,失效,验证,更新和清除。这并不是Adobe官方的条目,但我们把这些阶段组织在一起并分类,有利于帮我们定义组件什么时候做什么样的事件。我们的七个阶段按四个生命阶段来分。出生阶段由构建,添加,初始化组成。生长和成熟阶段由失效,验证,更新来组成。死亡就是清除阶段。来看一下第一个阶段:构建。组件阶段:构建(出生)构建阶段是组件的第一个阶段,定义为组件的构建。AdobeFlex文档用Button作为它们的示例控件,如下:viewplaincopytoclipboardprint?varmyButton:Button=newButton();//这是我们的构建阶段varmyButton:Button=newButton();//这是我们的构建阶段很明显的,对吧?什么是不很明显的,在这一阶段很少发生。在构建阶段会发生什么呢?如果我们从Button's的构造器中开始,跟进super()方法链,先到UIComponent类,然后到FlexSprite,最后到Sprite(到这里为止,因为它的源码包含在playerglobal.swc中)。FlexSprite是在继承链中是足够的顶端了,可以从这里看清整个构建过程了,所以我们就从这里开始。查看FlexSprite的构造器所做的工作仅是给name属性赋值了一个唯一的文本值。因为FlexSprite构造器所发生的情况已结束,我们再往下看UIComponent构造器。UIComponent在构造时做得稍微多一点,也不很多。设置焦点管理,开始跟踪自己的添加和删除事件,侦听焦点事件、键盘事件,定义自己的资源管理索引,保存当前组件自己的私有宽度和高度值,这些宽度和高度也许已在父类中定义。只做了这么多,没有布局、样式和子类的创建。最后,我们回到Button类。Button的构造函数里所做的是注册侦听MouseEvents的事件来处理用户的交互。只有这么多。关于在自定义组件开发中使用构造器,在后面的章节‘使用构造’最佳实践会有更详细的信息说明。以上是我们的构造阶段,下一阶段是什么?组件阶段:添加(出生)当你将组件添加上父组件时,就发生了‘添加’阶段:viewplaincopytoclipboardprint?this.addChild(myButton);//这是添加阶段的开始this.addChild(myButton);//这是添加阶段的开始将组件添加到父类这个过程中,父类要执行大量