AI You Can Trust - Ensuring Success with Data Integrity Webinar
Diving Deep with the Flex Component Life Cycle
1. Diving Deep with the Flex
Component Lifecycle
Joshua Jamison
EffectiveUI
www.effectiveui.com
January 30, 2009
2. Who am I?
‣ Joshua Jamison
Software Architect @ EffectiveUI
•
3. 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
5. 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
6. Elastic Racetrack: introduction
image courtesy of Ted Patrick
‣ Flex component lifecycle is built on this
frame model
‣ More on this later
7. A frame in AS3
image courtesy of Sean Christmann
8. Phases of the Lifecycle
‣ 3 Main Phases:
‣ BIRTH:
construction, con guration,
•
attachment, initialization
‣ LIFE:
• invalidation, validation, interaction
‣ DEATH:
• detachment, garbage collection
10. Construction
Birth
construction
con guration
attachment
initialization
Life
Death
11. What is a constructor?
‣ A function called to instantiate (create in
memory) a new instance of a class
Birth
construction
con guration
attachment
initialization
Life
Death
12. How is a constructor invoked?
Actionscript:
var theLabel : Label = new Label();
MXML:
<mx:Label id=quot;theLabelquot;/>
Birth
construction
con guration
attachment
initialization
Life
Death
13. What does a constructor have access to?
‣ Properties on the class
‣ Methods on the class
‣ Children have not yet been created!
Birth
construction
con guration
attachment
initialization
Life
Death
14. What does an ActionScript3
constructor look like?
public function ComponentName()
{
super();
//blah blah blah
}
‣ 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
Birth
construction
con guration ‣ Calls super() to invoke superclass constructor; if
attachment
you don’t, the compiler will!
initialization
Life
Death
15. What does an MXML constructor
look like?
‣ No need to de ne 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
Birth
compiler arguments and look at the
construction
con guration
generated code to verify this.
attachment
initialization
Life
Death
16. 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 speci c 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
Birth
object.
construction
con guration
attachment
initialization
Life
Death
17. 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
Birth
construction
con guration
attachment
initialization
Life
Death
18. Con guration
Birth
construction
con guration
attachment
initialization
Life
Death
19. Con guration
‣ 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=quot;value!quot;/>
Birth
construction
con guration
attachment
initialization
Life
Death
20. Hooray: Sample code!
<mx:Application ...>
...
<local:SampleChild property1=quot;value!quot;/>
</mx:Application>
Output:
SampleChild constructor
SampleChild.property1 setter
Birth
Adding child SampleChild4
construction
con guration
attachment
initialization
Life
Death
21. Con guration and Containers
‣ Containers must not expect their children
have to be instantiated when properties
are set.
<mx:Application ...>
<local:SampleContainer property1=quot;value!quot;>
<local:SampleChild property1=quot;value!quot;/>
</local:SampleContainer>
</mx:Application>
SampleContainer constructor
Birth
SampleContainer.property1 setter
construction
SampleChild constructor
con guration
attachment SampleChild.property1 setter
initialization
Life
Death
22. Con guration 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
Birth
construction
con guration
attachment
initialization
Life
Death
23. Attachment
Birth
construction
con guration
attachment
initialization
Life
Death
24. What is attachment?
‣ Adding a component to the display list
(addChild, addChildAt, MXML declaration)
‣ The component lifecycle is stalled after
con guration until attachment occurs.
Birth
construction
con guration
attachment
initialization
Life
Death
25. Consider this component:
public class A extends UIComponent
(It traces all of its methods.)
{
public function A() {
trace( quot;CONSTRUCTORquot; );
super();
}
override protected function createChildren() : void {
trace( quot;CREATECHILDRENquot; );
super.createChildren();
}
override protected function measure() : void {
trace( quot;MEASUREquot; );
super.measure();
}
override protected function updateDisplayList(width:Number, height:Number) : void {
trace( quot;UPDATEDISPLAYLISTquot; );
super.updateDisplayList(width,height);
}
override protected function commitProperties():void {
trace( quot;COMMITPROPERTIESquot; );
super.commitProperties();
}
26. And this application:
<mx:Application ...>
<mx:Script>
<![CDATA[
override protected function createChildren() : void {
super.createChildren();
var a : A = new A();
}
]]>
</mx:Script>
</mx:Application>
Output: CONSTRUCTOR
‣ Without attachment, the rest of the lifecycle
doesn’t happen.
27. But what about this application?
<mx:Application ...>
<mx:Script>
<![CDATA[
override protected function createChildren() : void {
super.createChildren();
var a : A = new A();
this.addChild( a );
}
]]>
</mx:Script>
Output: CONSTRUCTOR
</mx:Application>
CREATECHILDREN
COMMITPROPERTIES
MEASURE
UPDATEDISPLAYLIST
‣ Moral of the story: don’t add components to the
stage until you need them.
28. Initialization
Birth
construction
con guration
attachment
initialization
Life
Death
29. Initialization
‣ 2 phases, 3 events:
1. ‘preInitialize’ dispatched
Create 2. createChildren(); called
3. ‘initialize’ dispatched
Validate 4. rst validation pass occurs
5. ‘creationComplete’ dispatched
Birth
construction
con guration
attachment
initialization
Life
Death
30. 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,
•
con gure, attach
override protected function createChildren():void
{
...
create textField = new UITextField();
textField.enabled = enabled;
con gure textField.ignorePadding = true;
textField.addEventListener(quot;textFieldStyleChangequot;,
textField_textFieldStyleChangeHandler);
...
...
attach addChild(DisplayObject(textField));
}
31. rst validation pass
‣ Invalidation is not part of initialization -
only Validation
‣ Validation consists of 3 methods:
• commitProperties()
• measure()
• updateDisplayList()
‣ more on these later
Birth
construction
con guration
attachment
initialization
Life
Death
34. 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()
36. Deferment
‣ Deferment is the central concept to
understand in the component Life-cycle
‣ Use private variables and boolean ags to
defer setting any render-related
properties until the proper validation
method
37. Text-book example
Bad:
public function set text(value:String):void
{
myLabel.text = value;
// Possible Error! during first config phase,
// myLabel might not exist!
}
Good:
private var _text:String = quot;quot;;
override protected function
public function set text(value:String):void
{ commitProperties():void{
{
textSet = true;
_text = value; if(textChanged){
myLabel.text = _text;
textChanged = true;
textChanged = false;
}
invalidateProperties();
invalidateSize(); super.commitProperties();
}
invalidateDisplayList();
}
38. The Elastic Racetrack revisited
image courtesy of Sean Christmann
Invalidation occurs here
39. Invalidation methods
‣ invalidateProperties()
Any property changes
•
‣ invalidateSize()
• Changes to width or height
‣ invalidateDisplayList()
• Changes to child component size or
position
Birth
Life
invalidation
validation
interaction
Death
40. 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=quot;Click me!quot; click=quot;onClick()quot;/>
<test:BadList id=quot;theListquot; dataProvider=quot;{arr}quot;/>
</mx:VBox>
</mx:Application>
41. Invalidation example 2
public 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
42. Invalidation example 3
public 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(); Result: commitProperties
this.addChild( l );
called only twice (once
}
during initialization)
this._dataProviderChanged = false;
}
}
private function dataProviderChangeHandler( e : Event ) : void {
this._dataProviderChanged = true;
this.invalidateProperties();
}
public function GoodList() {}
}
45. Validation
‣ Apply the changes deferred during
invalidation
‣ Update all visual aspects of the
application in preparation for the render
phase
‣ 3 methods:
• commitProperties()
• measure()
Birth
• updateDisplayList()
Life
invalidation
validation
interaction
Death
46. commitProperties()
‣ Ely says: “Calculate and commit the effects
of changes to properties and underlying
data.”
‣ Invoked rst - immediately before
measurement and layout
Birth
Life
invalidation
validation
interaction
Death
47. 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 screen
Birth
Life
invalidation
validation
interaction
Death
48. measure()
‣ Component calculates its preferred
(“default”) and minimum proportions
based on content, layout rules,
constraints.
‣ Measure is called bottom up - lowest
children rst
‣ Caused by “invalidateSize()”
‣ NEVER called for explicitly sized
Birth
components
Life
invalidation
validation
interaction
Death
49. overriding measure()
‣ Used for dynamic layout containers (VBox,
etc.)
‣ Use getExplicitOrMeasuredWidth() (or
height) to get child proportions
‣ ALWAYS called during initialization
‣ Call super.measure() rst!
‣ Set measuredHeight, measuredWidth for
the default values; measuredMinHeight
Birth
and measuredMinWidth for the minimum.
Life
invalidation
validation
interaction
Death
50. measure() cont.
‣ Not reliable - Framework optimizes away
any calls to measure it deems
“unecessary”
Birth
Life
invalidation
validation
interaction
Death
51. 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
Birth
Life
invalidation
validation
interaction
Death
52. Overriding updateDisplayList()
‣ Usually call super.updateDisplayList() rst
• 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()
Birth
Life
invalidation
validation
interaction
Death
53. Elastic Racetrack cont.
‣ User Actions
Dispatch invalidation events
•
• Interact with any non-validation events
from this frame (mouse movements,
timers, etc.)
54. Elastic Racetrack Cont.
‣ Invalidate Action
Process all validation calls
•
‣ Render Action
• Do the heavy lifting - actually draw on
the screen
57. 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
“ res” or “dispatches” the event
‣ If another component wants to know
when something happens, it “listens” for
events
Birth
‣ Event-based architecture is loosely-
Life
invalidation
coupled
validation
interaction
Death
58. Bene ts of Loosely-Coupled
Architectures
‣ Everything becomes more reusable
‣ Components don’t have to know anything
about the components in which they’re
used
Birth
Life
invalidation
validation
interaction
Death
59. Who can dispatch events?
‣ Subclasses of EventDispatcher
EventDispatcher inherits directly from
•
Object
‣ Simply call dispatchEvent(event) to re off
an event when something happens
Birth
Life
invalidation
validation
interaction
Death
60. How to tell events apart?
‣ Event class
Different classes allow for customized
•
payloads
‣ “type” eld: a constant
Birth
Life
invalidation
validation
interaction
Death
61. Common Events
‣ Event.CHANGE
‣ MouseEvent.CLICK
‣ FlexEvent.CREATION_COMPLETE
‣ Event.RESIZE
‣ MouseEvent.ROLL_OUT
Birth
Life
invalidation
validation
interaction
Death
62. Handling Events
‣ <mx:Button id=”theButton”
click=”callThisFunction(event)”/>
‣ theButton.addEventListener( MouseEvent
.CLICK, callThisFunction )
Birth
Life
invalidation
validation
interaction
Death
63. Event Propagation
‣ Three phases: Capturing, Targeting, Bubbling
Application Application
Capturing Bubbling
Phase Phase
Target
Targeting
Phase
Birth
Life
invalidation
validation
interaction
Death
65. Event Propagation
----------------------------------------------------------
TARGET: button
CURRENT TARGET: eventTest
PHASE: CAPTURE
----------------------------------------------------------
TARGET: button
CURRENT TARGET: outer
PHASE: CAPTURE
----------------------------------------------------------
TARGET: button
CURRENT TARGET: inner
PHASE: CAPTURE
----------------------------------------------------------
TARGET: button
CURRENT TARGET: button
PHASE: TARGET
----------------------------------------------------------
TARGET: button
CURRENT TARGET: inner
PHASE: BUBBLE
----------------------------------------------------------
TARGET: button
CURRENT TARGET: outer
PHASE: BUBBLE
----------------------------------------------------------
Birth TARGET: button
CURRENT TARGET: eventTest
Life
PHASE: BUBBLE
invalidation
validation
interaction
Death
66. Stopping events from propagating
‣ stopPropagation() : Prevents processing
of any event listeners in nodes
subsequent to the current node in the
event ow
‣ stopImmediatePropagation() : Prevents
processing of any event listeners in the
current node and any subsequent nodes
in the event ow
Birth
Life
invalidation
validation
interaction
Death
67. target vs. currentTarget
‣ target: the object that dispatched the
event (doesn’t change)
‣ currentTarget: the object who is currently
being checked for speci c event listeners
(changes)
Birth
Life
invalidation
validation
interaction
Death
68. Dispatching events from custom
components
‣ MXML:
<mx:Metadata>
[Event(name=quot;atePizzaquot;, type=quot;flash.events.JoshEventquot;)]
</mx:Metadata>
‣ Actionscript:
[Event(name=quot;atePizzaquot;, type=quot;flash.events.JoshEventquot;)]
public class MyComponent extends UIComponent
{
...
}
Birth
Life
invalidation
validation
interaction
Death
71. 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-
Birth
collected
Life
Death
detachment
garbage
collection
72. 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 false
Birth
Life
Death
detachment
garbage
collection
74. 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
‣ Talk to Grant Skinner about forcing GC
Birth http://gskinner.com/blog/archives/2006/08/as3_resource_ma_2.html
•
Life
Death
detachment
garbage
collection
75. Conclusion
‣ Defer, Defer, DEFER!
‣ Use validation methods correctly
‣ Remember the elastic racetrack
76. References
‣ Ely Green eld: “Building a Flex Component”
• http://www.on ex.org/ACDS/
BuildingAFlexComponent.pdf
‣ Cha c 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