82
Diving Deep with the Flex Component Lifecycle Brad Umbaugh and RJ Owen

Adobe Flex Component Lifecycle

  • Upload
    rj-owen

  • View
    28.341

  • Download
    0

Embed Size (px)

DESCRIPTION

A presentation given about the Adobe Flex component lifecycle by Brad Umbaugh and RJ Owen at 360 Flex in summer 2008, San Jose.

Citation preview

Page 1: Adobe Flex Component Lifecycle

Diving Deep with the Flex Component Lifecycle

Brad Umbaugh and RJ Owen

Page 2: Adobe Flex Component Lifecycle

Who are we?

‣Brad Umbaugh• Senior Dev @ EffectiveUI, did globe on

Discovery Earth Live, the sort of guy you bring home to Mom.

‣RJ Owen• Senior Dev @ EffectiveUI, Adobe

Community Expert, Earth Live, done lots of projects, etc. etc. blah blah blah.

Page 3: Adobe Flex Component Lifecycle

Who are you (hopefully)?

‣Beginner to intermediate level developers‣Anyone who doesn’t currently understand

the lifecycle‣Anyone who wants a good review of the

basics

Page 4: Adobe Flex Component Lifecycle

What’s this about, anyway?

‣Flex component lifecycle (duh)‣Flex frame cycle (“elastic racetrack”)‣Brad’s inability to grow a real beard

Page 5: Adobe Flex Component Lifecycle

Flex

‣What is Flex?• A set of components• MXML• The component lifecycle!

Page 6: Adobe Flex Component Lifecycle

Flex Component Lifecycle

‣What is it?• The way the framework interacts with

every Flex component• A set of methods the framework calls to

instantiate, control, and destroy components

• Methods that make the most of the elastic racetrack

Page 7: Adobe Flex Component Lifecycle

Elastic Racetrack: introduction

‣Flex component lifecycle is built on this frame model‣More on this later

image courtesy of Ted Patrick

Page 8: Adobe Flex Component Lifecycle

A frame in AS3

image courtesy of Sean Christmann

Page 9: Adobe Flex Component Lifecycle

Phases of the Lifecycle

‣3 Main Phases:‣ BIRTH:

• construction, configuration, attachment, initialization

‣ LIFE: • invalidation, validation, interaction

‣DEATH: • detachment, garbage collection

Page 10: Adobe Flex Component Lifecycle

BirthCongratulations: You’re about to have a component.

Page 11: Adobe Flex Component Lifecycle

Construction

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 12: Adobe Flex Component Lifecycle

What is a constructor?

‣A function called to instantiate (create in memory) a new instance of a class

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 13: Adobe Flex Component Lifecycle

How is a constructor invoked?

Actionscript:

<mx:Label id="theLabel"/>MXML:

var theLabel : Label = new Label();

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 14: Adobe Flex Component Lifecycle

What does a constructor have access to?

‣Properties on the class‣Methods on the class‣Children have not yet been created!

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 15: Adobe Flex Component Lifecycle

What does an ActionScript3 constructor look like?

‣ No required arguments (if it will be used in MXML); zero, or all optional

‣ Only one per class (no overloading!)‣ No return type‣ Must be public‣ Calls super() to invoke superclass constructor; if

you don’t, the compiler will!

public function ComponentName(){super();//blah blah blah

}

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 16: Adobe Flex Component Lifecycle

What does an MXML constructor look like?

‣No need to define one. In fact, if you try to put one in an <mx:Script> block, you’ll get an error. ‣Why? Remember: MXML = Actionscript. A

constructor is created by the compiler in the Actionscript generated from the MXML.‣Specify “-keep” in the Flex Builder

compiler arguments and look at the generated code to verify this.

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 17: Adobe Flex Component Lifecycle

What should a constructor do?

‣Not much. Since the component’s children have not yet been created, there’s not much that can be done.‣There are specific methods (such as

createChildren) that should be used for most of the things you’d be tempted to put in a constructor.‣A good place to add event listeners to the

object.Birth

constructionconfigurationattachmentinitialization

LifeDeath

Page 18: Adobe Flex Component Lifecycle

Don’t create or attach children in the constructor

‣ It’s best to delay the cost of createChildren calls for added children until it’s necessary

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 19: Adobe Flex Component Lifecycle

Configuration

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 20: Adobe Flex Component Lifecycle

Configuration

‣The process of assigning values to properties on objects‣ In MXML, properties are assigned in this

phase, before components are attached or initialized<local:SampleChild property1="value!"/>

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 21: Adobe Flex Component Lifecycle

Hooray: Sample code!

SampleChild constructorSampleChild.property1 setterAdding child SampleChild4

<mx:Application ...> ...<local:SampleChild property1="value!"/>

</mx:Application>

Output:

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 22: Adobe Flex Component Lifecycle

Configuration and Containers

‣Containers must not expect their children have to be instantiated when properties are set.<mx:Application ...> <local:SampleContainer property1="value!"> <local:SampleChild property1="value!"/> </local:SampleContainer></mx:Application>

SampleContainer constructorSampleContainer.property1 setterSampleChild constructorSampleChild.property1 setter

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 23: Adobe Flex Component Lifecycle

Configuration Optimization

‣To avoid performance bottlenecks, make your setters fast and defer any real work until validation‣We’ll talk more about deferment in the

validation / invalidation section

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 24: Adobe Flex Component Lifecycle

Attachment

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 25: Adobe Flex Component Lifecycle

What is attachment?

‣Adding a component to the display list (addChild, addChildAt, MXML declaration)

‣The component lifecycle is stalled after configuration until attachment occurs.

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 26: Adobe Flex Component Lifecycle

Consider this component: public class A extends UIComponent { public function A() { trace( "CONSTRUCTOR" ); super(); } override protected function createChildren() : void { trace( "CREATECHILDREN" ); super.createChildren(); } override protected function measure() : void { trace( "MEASURE" ); super.measure(); } override protected function updateDisplayList(width:Number, height:Number) : void { trace( "UPDATEDISPLAYLIST" ); super.updateDisplayList(width,height); } override protected function commitProperties():void { trace( "COMMITPROPERTIES" ); super.commitProperties(); }

(It traces all of its methods.)

Page 27: Adobe Flex Component Lifecycle

And this application:

‣Without attachment, the rest of the lifecycle doesn’t happen.

<mx:Application ...> <mx:Script> <![CDATA[ override protected function createChildren() : void { super.createChildren(); var a : A = new A(); } ]]> </mx:Script></mx:Application>

CONSTRUCTOROutput:

Page 28: Adobe Flex Component Lifecycle

But what about this application?

‣Moral of the story: don’t add components to the stage until you need them.

<mx:Application ...> <mx:Script> <![CDATA[ override protected function createChildren() : void { super.createChildren(); var a : A = new A();

this.addChild( a ); } ]]> </mx:Script></mx:Application> Output: CONSTRUCTOR

CREATECHILDRENCOMMITPROPERTIESMEASUREUPDATEDISPLAYLIST

Page 29: Adobe Flex Component Lifecycle

Initialization

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 30: Adobe Flex Component Lifecycle

Initialization

‣2 phases, 3 events:

1. ‘preInitialize’ dispatched2. createChildren(); called3. ‘initialize’ dispatched4. first validation pass occurs5. ‘creationComplete’ dispatched

Create

Validate

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 31: Adobe Flex Component Lifecycle

createChildren()‣ MXML uses the createChildren() method to add

children to containers‣ Override this method to add children using AS

• Follow MXML’s creation strategy: create, configure, attach

override protected function createChildren():void{

...textField = new UITextField();

textField.enabled = enabled;textField.ignorePadding = true;textField.addEventListener("textFieldStyleChange", textField_textFieldStyleChangeHandler);

... ... addChild(DisplayObject(textField));}

create

configure

attach

Page 32: Adobe Flex Component Lifecycle

createChildren() cont.

‣Defer creating dynamic and data-driven components until commitProperties()

‣UIComponent.createChildren() is empty, but it’s good practice to always call super.createChildren() anyway

Birthconstructionconfigurationattachmentinitialization

LifeDeath

Page 33: Adobe Flex Component Lifecycle

first validation pass

‣ Invalidation is not part of initialization - only Validation‣Validation consists of 3 methods:

• commitProperties()• measure()• updateDisplayList()

‣more on these laterBirth

constructionconfigurationattachmentinitialization

LifeDeath

Page 34: Adobe Flex Component Lifecycle

LifeThey grow up so fast.

Page 35: Adobe Flex Component Lifecycle

Invalidation

BirthLife

invalidationvalidationinteraction

Death

Page 36: Adobe Flex Component Lifecycle

Invalidation / Validation cycle

‣ Flex imposes deferred validation on the Flash API• goal: defer screen updates until all

properties have been set‣ 3 main method pairs to be aware of:

• invalidateProperties() -> commitProperties()

• invalidateSize() -> measure()• invalidateDisplayList() ->

updateDisplayList()

Page 37: Adobe Flex Component Lifecycle

Invalidation / Validation theory

‣First, a little theory.

Page 38: Adobe Flex Component Lifecycle

Deferment

‣Deferment is the central concept to understand in the component Life-cycle

‣Use private variables and boolean flags to defer setting any render-related properties until the proper validation method

Page 39: Adobe Flex Component Lifecycle

Text-book example

public function set text(value:String):void{ myLabel.text = value;// Possible Error! during first config phase, // myLabel might not exist!}

private var _text:String = "";public function set text(value:String):void{ textSet = true; _text = value; textChanged = true;

invalidateProperties(); invalidateSize(); invalidateDisplayList();}

override protected function commitProperties():void{{

if(textChanged){myLabel.text = text;textChanged = false;

}super.commitProperties();

}

Bad:

Good:

Page 40: Adobe Flex Component Lifecycle

The Elastic Racetrack revisited

Invalidation occurs here

image courtesy of Sean Christmann

Page 41: Adobe Flex Component Lifecycle

Invalidation methods

‣ invalidateProperties()• Any property changes

‣ invalidateSize()• Changes to width or height

‣ invalidateDisplayList()• Changes to child component size or

positionBirthLife

invalidationvalidationinteraction

Death

Page 42: Adobe Flex Component Lifecycle

Invalidation example 1<mx:Application> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; [Bindable] public var arr : ArrayCollection = new ArrayCollection(); public function onClick() : void { var c : int = 0; while( c++ < 20 ) { arr.addItem( c ); } } ]]> </mx:Script> <mx:VBox> <mx:Button label="Click me!" click="onClick()"/> <test:BadList id="theList" dataProvider="{arr}"/> </mx:VBox></mx:Application>

Page 43: Adobe Flex Component Lifecycle

Invalidation example 2public class BadList extends VBox

{ private var _dataProvider : ArrayCollection; public function set dataProvider( arr : ArrayCollection ) : void { this._dataProvider = arr; arr.addEventListener( CollectionEvent.COLLECTION_CHANGE, dataProviderChangeHandler ); } private function dataProviderChangeHandler( e : Event ) : void { this.removeAllChildren(); for each( var n : Number in this._dataProvider ) { var l : Label = new Label(); l.text = n.toString(); this.addChild( l );

} } public function BadList() {} }

Result: dataProviderChangeHandler called 20 times

Page 44: Adobe Flex Component Lifecycle

Invalidation example 3public class GoodList extends VBox

{ private var _dataProvider : ArrayCollection; private var _dataProviderChanged : Boolean = false; public function set dataProvider( arr : ArrayCollection ) : void { this._dataProvider = arr; arr.addEventListener( CollectionEvent.COLLECTION_CHANGE, dataProviderChangeHandler ); this._dataProviderChanged = true; this.invalidateProperties(); } override protected function commitProperties():void { super.commitProperties(); if( this._dataProviderChanged ) { this.removeAllChildren(); for each( var n : Number in this._dataProvider ) { var l : Label = new Label(); l.text = n.toString(); this.addChild( l ); } this._dataProviderChanged = false; } } private function dataProviderChangeHandler( e : Event ) : void { this._dataProviderChanged = true; this.invalidateProperties(); } public function GoodList() {} }

Result: commitProperties called only twice (once during initialization)

Page 45: Adobe Flex Component Lifecycle

Validation

BirthLife

invalidationvalidationinteraction

Death

Page 46: Adobe Flex Component Lifecycle

The Elastic Racetrack revisited

Validation occurs here

Page 47: Adobe Flex Component Lifecycle

Validation

‣Apply the changes deferred during invalidation‣Update all visual aspects of the

application in preparation for the render phase‣3 methods:

• commitProperties()• measure()• updateDisplayList()

BirthLife

invalidationvalidationinteraction

Death

Page 48: Adobe Flex Component Lifecycle

commitProperties()

‣Ely says: “Calculate and commit the effects of changes to properties and underlying data.”‣ Invoked first - immediately before

measurement and layout

BirthLife

invalidationvalidationinteraction

Death

Page 49: Adobe Flex Component Lifecycle

commitProperties() cont.

‣ALL changes based on property and data events go here‣Even creating and destroying children, so

long as they’re based on changes to properties or underlying data‣Example: any list based component with

empty renderers on the screenBirthLife

invalidationvalidationinteraction

Death

Page 50: Adobe Flex Component Lifecycle

measure()

‣Component calculates its preferred (“default”) and minimum proportions based on content, layout rules, constraints.‣Measure is called bottom up - lowest

children first‣Caused by “invalidateSize()”‣NEVER called for explicitly sized

componentsBirthLife

invalidationvalidationinteraction

Death

Page 51: Adobe Flex Component Lifecycle

overriding measure()

‣Used for dynamic layout containers (VBox, etc.)‣Use getExplicitOrMeasuredWidth() (or

height) to get child proportions‣ALWAYS called during initialization‣Call super.measure() first!‣Set measuredHeight, measuredWidth for

the default values; measuredMinHeight and measuredMinWidth for the minimum.

BirthLife

invalidationvalidationinteraction

Death

Page 52: Adobe Flex Component Lifecycle

measure() cont.

‣Not reliable - Framework optimizes away any calls to measure it deems “unecessary”‣Ely says: “Start by explicitly sizing your

component and implement this later.”

BirthLife

invalidationvalidationinteraction

Death

Page 53: Adobe Flex Component Lifecycle

updateDisplayList()

‣All drawing and layout code goes here, making this the core method for all container objects‣Caused by invalidateDisplayList();‣Concerned with repositioning and

resizing children‣updateDisplayList() is called top-down

BirthLife

invalidationvalidationinteraction

Death

Page 54: Adobe Flex Component Lifecycle

Overriding updateDisplayList()

‣Usually call super.updateDisplayList() first• super() is optional - don’t call it if you’re

overriding everything it does‣Size and lay out children using move(x,y)

and setActualSize(w,h) if possible• I never have good luck with

setActualSize()BirthLife

invalidationvalidationinteraction

Death

Page 55: Adobe Flex Component Lifecycle

Elastic Racetrack cont.

‣User Actions• Dispatch invalidation events• Interact with any non-validation events

from this frame (mouse movements, timers, etc.)

Page 56: Adobe Flex Component Lifecycle

Elastic Racetrack Cont.

‣ Invalidate Action• Process all validation calls

‣Render Action• Do the heavy lifting - actually draw on

the screen

Page 57: Adobe Flex Component Lifecycle

The Elastic Racetrack revisited

Queued InvalidationDeferred Validation

Render!

Page 58: Adobe Flex Component Lifecycle

Interaction

BirthLife

invalidationvalidationinteraction

Death

Page 59: Adobe Flex Component Lifecycle

How do objects know when something happens?

‣Events: objects passed around when anything interesting goes on (clicks, moves, changes, timers...)‣ If something happens to a component, it

“fires” or “dispatches” the event‣ If another component wants to know

when something happens, it “listens” for events‣Event-based architecture is loosely-

coupled

BirthLife

invalidationvalidationinteraction

Death

Page 60: Adobe Flex Component Lifecycle

Benefits of Loosely-Coupled Architectures

‣Everything becomes more reusable‣Components don’t have to know anything

about the components in which they’re used

BirthLife

invalidationvalidationinteraction

Death

Page 61: Adobe Flex Component Lifecycle

Who can dispatch events?

‣Subclasses of EventDispatcher• EventDispatcher inherits directly from

Object‣Simply call dispatchEvent(event) to fire off

an event when something happens

BirthLife

invalidationvalidationinteraction

Death

Page 62: Adobe Flex Component Lifecycle

How to tell events apart?

‣Event class• Different classes allow for customized

payloads‣ “type” field: a constant

BirthLife

invalidationvalidationinteraction

Death

Page 63: Adobe Flex Component Lifecycle

Common Events

‣Event.CHANGE‣MouseEvent.CLICK‣FlexEvent.CREATION_COMPLETE‣Event.RESIZE‣MouseEvent.ROLL_OUT

BirthLife

invalidationvalidationinteraction

Death

Page 64: Adobe Flex Component Lifecycle

Handling Events

‣<mx:Button id=”theButton” click=”callThisFunction(event)”/>‣ theButton.addEventListener( MouseEvent

.CLICK, callThisFunction )

BirthLife

invalidationvalidationinteraction

Death

Page 65: Adobe Flex Component Lifecycle

Event Propagation

Capturing Phase

Target

Application Application

Bubbling Phase

Targeting Phase

‣ Three phases: Capturing, Targeting, Bubbling

BirthLife

invalidationvalidationinteraction

Death

Page 66: Adobe Flex Component Lifecycle

Event Propagation‣ Three phases: Capturing, Targeting, Bubbling

<mx:Application initialize="onInitialize()"><mx:Script> <![CDATA[ public function onInitialize() : void { this.addEventListener( MouseEvent.CLICK, clickHandler, true ); this.addEventListener( MouseEvent.CLICK, clickHandler, false ); outer.addEventListener( MouseEvent.CLICK, clickHandler, true ); outer.addEventListener( MouseEvent.CLICK, clickHandler, false ); inner.addEventListener( MouseEvent.CLICK, clickHandler, true ); inner.addEventListener( MouseEvent.CLICK, clickHandler, false ); button.addEventListener( MouseEvent.CLICK, clickHandler, true ); button.addEventListener( MouseEvent.CLICK, clickHandler, false ); } public function clickHandler( e : Event ) : void { trace("----------------------------------------------------------"); trace("TARGET: " + e.target.id ); trace("CURRENT TARGET: " + e.currentTarget.id ); trace("PHASE: " + ( e.eventPhase == 1 ? "CAPTURE" : ( e.eventPhase == 2 ? "TARGET" : "BUBBLE" ) ) ); } ]]></mx:Script> <mx:VBox> <mx:Panel id="outer"> <mx:TitleWindow id="inner"> <mx:Button id="button"/> </mx:TitleWindow> </mx:Panel> </mx:VBox></mx:Application>

BirthLife

invalidationvalidationinteraction

Death

Page 67: Adobe Flex Component Lifecycle

Event Propagation----------------------------------------------------------TARGET: buttonCURRENT TARGET: eventTestPHASE: CAPTURE----------------------------------------------------------TARGET: buttonCURRENT TARGET: outerPHASE: CAPTURE----------------------------------------------------------TARGET: buttonCURRENT TARGET: innerPHASE: CAPTURE----------------------------------------------------------TARGET: buttonCURRENT TARGET: buttonPHASE: TARGET----------------------------------------------------------TARGET: buttonCURRENT TARGET: innerPHASE: BUBBLE----------------------------------------------------------TARGET: buttonCURRENT TARGET: outerPHASE: BUBBLE----------------------------------------------------------TARGET: buttonCURRENT TARGET: eventTestPHASE: BUBBLE

BirthLife

invalidationvalidationinteraction

Death

Page 68: Adobe Flex Component Lifecycle

Stopping events from propagating

‣ stopPropagation() : Prevents processing of any event listeners in nodes subsequent to the current node in the event flow‣ stopImmediatePropagation() : Prevents

processing of any event listeners in the current node and any subsequent nodes in the event flow

BirthLife

invalidationvalidationinteraction

Death

Page 69: Adobe Flex Component Lifecycle

target vs. currentTarget

‣ target: the object that dispatched the event (doesn’t change)‣ currentTarget: the object who is currently

being checked for specific event listeners (changes)

BirthLife

invalidationvalidationinteraction

Death

Page 70: Adobe Flex Component Lifecycle

Dispatching events from custom components

‣MXML:

‣Actionscript:

<mx:Metadata> [Event(name="atePizza", type="flash.events.BradEvent")]</mx:Metadata>

[Event(name="atePizza", type="flash.events.BradEvent")]public class MyComponent extends UIComponent{ ...}

BirthLife

invalidationvalidationinteraction

Death

Page 71: Adobe Flex Component Lifecycle

gutterShark: event manager

‣guttershark is Aaron Smith’s “Actionscript 3 Productivity Library”‣Contains a lot of stuff commonly needed

when developing Flash web sites‣ Includes an EventManager that’s helpful

BirthLife

invalidationvalidationinteraction

Death

Page 72: Adobe Flex Component Lifecycle

gutterShark: event manager

BirthLife

invalidationvalidationinteraction

Death

<mx:Application initialize="onInitialize()"><mx:Script> <![CDATA[ import net.guttershark.events.EventManager; private var em : EventManager; public function onInitialize( e : Event = null ) : void { em = EventManager.gi(); em.handleEvents(theButton,this,"onButton"); } public function onButtonClick() : void { theLabel.text = "CLICK"; } public function onButtonMouseOver() : void { theLabel.text = "MOUSE OVER"; } public function onButtonMouseOut() : void { theLabel.text = "MOUSE OUT"; } ]]></mx:Script> <mx:VBox> <mx:Button id="theButton" label="Click Me!"/> <mx:Label id="theLabel"/> </mx:VBox></mx:Application>

Page 73: Adobe Flex Component Lifecycle

gutterShark: event manager

‣Supports many different types of events and the addition of more‣Easy integration with Google Analytics,

Atlas, Webtrends‣http://www.guttershark.net

BirthLife

invalidationvalidationinteraction

Death

Page 74: Adobe Flex Component Lifecycle

DeathAll good things come to an end.

Page 75: Adobe Flex Component Lifecycle

Detachment

BirthLifeDeath

detachmentgarbage collection

Page 76: Adobe Flex Component Lifecycle

Detachment

‣ “Detachment” refers to the process of removing a child from the display list‣These children can be re-parented

(brought back to life) or abandoned to die‣Abandoned components don’t get

validation calls and aren’t drawn‣ If an abandoned component has no more

active references, it *should* be garbage-collected

BirthLifeDeath

detachmentgarbage collection

Page 77: Adobe Flex Component Lifecycle

Detachment cont.

‣Re-parenting isn’t cheap, but it’s cheaper than re-creating the same component twice‣Children do not need to be removed from

their parent before being re-parented, but always should be‣Consider hiding rather than removing

• set visible and includeInLayout to falseBirthLifeDeath

detachmentgarbage collection

Page 78: Adobe Flex Component Lifecycle

Garbage Collection

BirthLifeDeath

detachmentgarbage collection

Page 79: Adobe Flex Component Lifecycle

Garbage Collection

‣The process by which memory is returned to the system‣Only objects with no remaining references

to them will be gc’d• Set references to detached children to

“null” to mark them for GC‣Unreliable, like Brad‣Talk to Grant Skinner about forcing GC

• http://gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html

BirthLifeDeath

detachmentgarbage collection

Page 80: Adobe Flex Component Lifecycle

Conclusion

‣Defer, Defer, DEFER!‣Use validation methods correctly‣Remember the elastic racetrack‣Always look on the bright side of

detachment.

Page 81: Adobe Flex Component Lifecycle

References‣ Ely Greenfield: “Building a Flex Component”• http://www.onflex.org/ACDS/

BuildingAFlexComponent.pdf

‣ Chafic Kazoun, Joey Lott: “Programming Flex 2” by O’Reilly• http://oreilly.com/catalog/9780596526894/

‣ Colin Moock: “Essential Actionscript 3.0” by O’Reilly• http://oreilly.com/catalog/9780596526948/

index.html