Flex Component Life Cycle

Now that we have a nice looking Preloader, we’re close to be ready starting developing new Flex Custom Components.

Anyway, before doing so, we need to clearly understand what is the Flex Component Life Cycle. A good understanding of this Life Cycle is really mandatory to be able to develop “correctly” a Flex Custom Component.

Code extracts are provided to illustrate the different principles discussed below, use the “See code sample” links to look at them, use the “See explanations” links to come back to the explanations.

The Flex Component Life Cycle can be defined as the mechanism used by the Flex framework to manage components during their life as part of an application. To be more precise, this Life Cycle applies to objects inheriting from UIComponent. UIComponent is the base class of all graphical objects that can be rendered on the screen and manipulated by users.

The UIComponent Life Cycle is composed of the following phases:

The following code samples illustrate the patterns and principles of the UIComponent Life Cycle:

Construction ( see code sample )

Construction is the first phase of a component and starts when the component’s constructor is called.

We all know what is a constructor and what’s its purpose. But, in Flex, there are a few specific points that are very important to know and understand.

Call super(): constructor rule #1

Even if it isn’t really mandatory (because the compiler will do it for you), it’s always better to call the constructor of your super class explicitly by calling super().

Zero argument: constructor rule #2

The constructor must have zero required arguments. The reason behind this rule is that components can be instantiated either from Action Script or from MXML. When creating a component from an MXML file, it is not possible to provide any parameter to the constructor.

So, if you want that your components can be used from MXML, stick to the rule!

As little work and calculation as possible: constructor rule #3

In contrary to many other languages, the Flex constructors shouldn’t be used for initializing or building the component’s pieces.

The reasons behind this rule are that, when the constructor is called, the component is far from being ready for configuration. Some of its properties are probably not yet created, none if its children components is created, some properties or styles could be changed by a subclass and the constructor code will never benefit from the Just-In-Time compilation process!

The constructor can be used to add event listeners or hard code some properties, but you should not put more here; do as little work and calculation in your constructors.

Attachment

Attachment is the next phase of a component and starts when the component is added to a parent component using addChild(). This is where the component is added in the Display List.

When the component is attached, three main steps occur:

  • addingChild(): child’s parent and document reference are set, LayoutManager is defined and style management is initialized.
  • $addChild(): this Flash Player method adds the component to the Display List.
  • childAdded(): determines if the component has already been initialized, if not it triggers the next phase by calling initialize().

Initialization ( see code sample )

Initialization phase of a component creates the children of the component and prepare it for the first Invalidation / Validation cycle.

When the component is initialized, five main steps occur:

  • FlexEvent.PREINITIALIZED event is dispatched.
  • createChildren() is called.
  • FlexEvent.INITIALIZED event is dispatched.
  • First Invalidation / Validation cycle.
  • FlexEvent.CREATION_COMPLETE event is dispatched.

As for the constructor, it is mandatory to understand in details what can be done, or not, in the createChildren() method.

This is the best place for adding children components that are required for the whole lifetime of the component. The children created dynamically or that are data-driven must not be created here but in the commitProperties() as we’ll see later.

Before instantiating a child component, always check if it doesn’t already exist to avoid multiple unnecessary creations to occur when createChildren() is called in a different scenario (re-parenting case for example).

Stick to the following pattern when creating your children:

  • Creation (using the new operator)
  • Configuration (setting child properties)
  • Attachment (calling addChild())

Please note that calling addChild() with one of your child starts the child Life Cycle Attachment phase that, in turn, can trigger creation of children of your child.

Invalidation / Validation

Invalidation / Validation phase is a phase that will get repeated many times during the component’s life. It is, in fact, more than a phase, it’s a cycle on its own.

The full cycle looks like:

  • Update phase:
    triggered whenever the user interacts with the components or when methods or properties are invoked/set.
  • Invalidation phase:
    triggered by the component methods after having changed its state or properties to implement the request from the user or the other component. It just informs the framework that we have some dirty data to be validated before rendering the component.
  • Validation phase:
    triggered by the Flex framework just before the Rendering phase. This is where the changes done in the previous step must be applied to the graphical elements of the component.
  • FlexEvent.UPDATE_COMPLETE event is dispatched
  • Rendering phase

This cycle is one of the most important aspect of Flex so let’s dig into more details. This cycle provides a decoupling process that separates the value changes (Invalidation, i.e. making the old value no longer valid) and the new values processing (Validation, i.e. applying the new values). This decoupling enforces defers the processing associated to value changes to the last moment: just before rendering the component. It helps avoiding multiple unnecessary processing as you can often find in many softwares.

For example, changing the width of the component can lead to heavy processing (since layout would be updated, leading to an update of the children width that would in turn trigger a layout update, …). If your suboptimal code would change the width twice in the same method, the heavy processing would be executed twice. With the Invalidation / Validation model, this would just invalidate the width twice, but the processing done in Validation would still be executed only once.

In other words: whenever the component decides to change any of its graphical characteristics, the Invalidation / Validation cycle must be used.

The Invalidation / Validation model is split into three steps corresponding to three pairs of methods:

  • Properties changes: invalidateProperties() => commitProperties()
  • Sizing changes: invalidateSize() => measure()
  • Drawing & Positioning changes: invalidateDisplayList() => updateDisplayList()

Properties changes ( see code sample )

To manage properties changes in the Flex-way, you must follow a simple pattern inside your setters relying on dirty flags and storage variables.

Don’t change any property directly, store the new value in a private storage variable instead and position a dirty flag indicating that the property is dirty (this principle applies to properties that have an impact on your component graphical representation).

Once done, just call the invalidateProperties() method and let the framework do its job.

During the Validation phase, inside the commitProperties() method, update the graphical properties using your private variables and reset the corresponding dirty flags.

The commitProperties() method is also the right place to add or remove children that aren’t required through the whole component life cycle (dynamic or data-driven children).

It should be noted that the properties mentioned here are the ones that are used by a component when computing its size or layout. If a given property doesn’t impact size or layout of the component, it can be updated without using the Invalidation / Validation pattern.

Sizing changes ( see code sample )

The principle described above for the Properties is applicable also to sizing changes. A typical case is when the styles associated to a component are modified. The styleChanged() method can be used to check whether the style changes are impacting the component size and, if it’s the case, call the invalidateSize() method.

Then, the processing to update the component size based on content and layout rules has to be done inside the measure() method. There are four properties that needs to be set: measuredWidth, measuredHeight, measuredMinWidth and measuredMinHeight.

It should be noted that sizing is determined from bottom to top, meaning that this will be implicitly triggered when a component child is resized.

Another important point to notice concerns the way to retrieve the children width or height. Don’t use their width and height properties directly but, instead, use getExplicitOrMeasuredWidth() or getExplicitOrMeasuredHeight() methods (for child of type UIComponent).

Drawing & Positioning changes ( see code sample )

The third category concerned by the Invalidation / Validation cycle is the drawing and positioning step that involves the two methods: invalidateDisplayList() and updateDisplayList().

Whenever a change in the component affects the way it’s drawn or positioned, just call invalidateDisplayList() to ensure that the updateDisplayList() method be called just before the rendering on screen phase.

The updateDisplayList() method is where the component is laid out.Size and position of each child (of type UIComponent) must be defined by calling their setActualSize() and move() methods . In contrary to a direct update of their x, y, width or height properties, setActualSize() and move() methods are aggregating the changes such that only one event is dispatched even if both x and y or width and height values are changed. If the child isn’t a UIComponent, you can then change directly the x, y, width or height properties.

It’s also quite common using the Flash Player drawing API in updateDisplayList().

Detachment

Detachment is one of last phase of a component and starts when the component is removed from a parent component using removeChild(). This is where the component is removed from the Display List.

When the component is detached, three main steps occur:

  • removingChild(): does nothing by default, can be overridden to do something before the component is removed from the display list.
  • $removeChild(): this Flash Player method removes the component from the Display List and checks if it still has any reference. If there are none, the component is marked for garbage collection.
  • childRemoved(): removes references to the components parent and document properties.

The Invalidation / Validation cycle won’t occur anymore for a detached component.

Keeping a reference to a component before detaching it will keep it alive, allowing to re-parent it (i.e. attaching it to another parent component) that is more efficient than re-instantiating a new component.

Destruction

Components marked for garbage collection are really destroyed (i.e. removed from memory) only when a garbage collection phase occurs. The garbage collection is fully handled by the Flash Player and you should never try to force it from your own code.

Anyway, there are common pitfalls leading to memory leaks because components aren’t deleted. The most common problems are with event listeners, dictionaries and timers that still maintain references to the component. So, whenever you want a component to die, don’t forget to make cleanup of any event listener, dictionary or timer that was referring to the component.

Flex Component Life Cycle Code Extracts

Here are some code extracts illustrating the different patterns and principles described above. You should be able to navigate easily between these extracts and the related explanations by using the “See explanations” and “See sample” links.

constructor ( see explanations )

//--------------------------------------------------------------------------
//
//  Constructor
//
// Rules:
//		call super()
//		0 required arguments (such that it can be used from MXML)
// 		as light as possible
//
//--------------------------------------------------------------------------
public function MyClass()
{
	super();

	// Minimal processing recommended.
	// Can hardcode some properties and add event listeners, but not more
	//
	//Example:
	addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
}

createChildren()( see explanations )

//--------------------------------------------------------------------------
//
// createChildren
//
// Place to create static composited components (the ones having the same
// life duration as the component). For dynamic and data-driven children,
// manage them in commitProperties().
//
// Rules:
//		call super()
//		check for child availability before creating it
//		follow the create, configure, listen, attach pattern for all children
//
//--------------------------------------------------------------------------

override protected function createChildren():void
{
	super.createChildren();

	// Before creating a child, check if it doesn't already exist
	if (!myChild) {
		// Create the child
		myChild = new MyChild();

		// Configure the child
		myChild.property = value;

		// Manage child event listeners
		myChild.addEventListener(MouseEvent.CLICK, onClickHandler);

		// Attach the child
		addChild(myChild);
	}
}

property change (setters, invalidateProperties() and commitProperties()) ( see explanations )

//--------------------------------------------------------------------------
//
// Property setter and getter
//
// For properties having a influence on the component size, layout or drawing,
// use the Invalidation / Validation principle.
//
// Rules:
//		check if value is really changing
//		use storage variable
//		use dirty flag
//		associate your own event to bindable properties
//
//--------------------------------------------------------------------------

private var _myProperty:String = "";
private var myPropertyChanged:Boolean;
[Bindable("myPropertyChanged")]

public function get myProperty():String
{
    return _myProperty;
}

public function set myProperty(value:String):void
{
	// Check if value is really a change
    if (_myProperty != value)
    {
    	// Keep in storage variable
        _myProperty = value;
        // Set dirty flag
        myPropertyChanged = true;
        // Indicate the Invalidation by calling invalidateProperties or invalidateSize or invalidateDisplayList
        invalidateProperties();
        // Dispatch event for bindable properties
        dispatchEvent(new Event("myPropertyChanged"));
    }
}

//--------------------------------------------------------------------------
//
// commitProperties
//
// Manage properties changes indicated by dirtyFlags. Create or Delete
// dynamic children if required.
//
// Rules:
//		call super
//		check dirtyFlag to see if validation is needed or not
//		use storage variable to commit the new value
//		reset dirty flag
//
//--------------------------------------------------------------------------

override protected function commitProperties():void
{
	super.commitProperties();

	// Check if our property is dirty or not
	if (myPropertyChanged)
	{
		// Commit the new value using the storage variable
		myChild.text = _myProperty;

		// Reset dirty flag
		myPropertyChanged = false;
	}
	// If needed, create or delete dynamic children based on a property change
}

sizing change (invalidateSize() and measure()) ( see explanations )

//--------------------------------------------------------------------------
//
// Style changes
//
// For properties having a influence on the component size, layout or drawing,
// use the Invalidation / Validation principle.
//
// Rules:
//		call super
//		check if style change impacts the component and trigger the right
//      invalidation method accordingly
//
//--------------------------------------------------------------------------

override public function styleChanged(styleProp:String):void
{
	super.styleChanged(styleProp);

	// Check the changed property
	if (styleProp == "myStyleProperty")
	{
		// Trigger corresponding invalidation methods
		invalidateSize();			// When size of the component is impacted by the new style value
		invalidateDisplayList();	// When drawing of the component is impacted by the new style value
	}
}

//--------------------------------------------------------------------------
//
// measure
//
// Compute measuredWidth/Height and optionally MeasuredMinWidth/Height.
//
// Rules:
//		call super
//		use getExplicitOrMeasuredWidth/Height to access child measures
//
//--------------------------------------------------------------------------

override protected function measure():void
{
	super.measure();

	// Calculate the size of the component and set measuredWidth and measuredHeight
	var myMargin:Number = getStyle("myMargin");
	if (!myMargin)
		myMargin = 5;

	// Here we have two children on top of each other so our width is the max of the
	// children's width and our height is the sum of the children's height + the margin.
	measuredWidth = Math.max(myChild1.getExplicitOrMeasuredWidth(), myChild2.getExplicitOrMeasuredWidth) + 2*myMargin;
	measuredHeight = myChild1.getExplicitOrMeasuredHeight() + myChild2.getExplicitOrMeasuredHeight() + 3*myMargin;

	// Min is often set to the measured values
	measuredMinWidth = measuredWidth;
	measuredMinHeight = measuredHeight;
}

drawing & positioning change (invalidateDisplayList() and updateDisplayList()) ( see explanations )

//--------------------------------------------------------------------------
//
// updateDisplayList
//
// Position and layout the children component. Use the Flash Player drawing
// API.
//
// Rules:
//		call super
//		call setActualSize on children to change their size
//		call move on children to change their position
//
//--------------------------------------------------------------------------

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
	super.updateDisplayList(unscaledWidth, unscaledHeight);

	// Position & Resize your children using move and setActualSize
	if (child1)
	{
		// Suppose we have just one child taking all the available area
    	child.move(0, 0);
    	child.setActualSize(unscaledWidth, unscaledHeight);
	}

	// Do some drawing using the Flash Player API
	graphics.clear();
	graphics.beginFill(0x00FF00);
	graphics.drawRect(0,0,unscaledWidth,usedHeight);
	graphics.endFill();
}
VN:F [1.9.8_1114]
Rating: 5.0/5 (18 votes cast)
Flex Component Life Cycle, 5.0 out of 5 based on 18 ratings
  1. October 1st, 2010 at 21:45 | #1

    Great Post, this helped me too much.
    Thanks a lot :)

    VA:F [1.9.8_1114]
    Rating: 4.0/5 (4 votes cast)
  2. Prasanth
    January 3rd, 2011 at 12:41 | #2

    Awesome post!
    Examples are aptly quoted.. They are very helpful in understanding the component life cycle :)

    VA:F [1.9.8_1114]
    Rating: 3.0/5 (2 votes cast)
  3. Justin Wang
    January 24th, 2011 at 08:15 | #3

    Awesome Post!
    This post helped me understanding the component life cycle very clearly.
    Thanks a lot.

    VA:F [1.9.8_1114]
    Rating: 3.7/5 (3 votes cast)
  4. Ashish
    May 17th, 2011 at 07:56 | #4

    Dear JYC,

    I wish you will be in best of your time and health. Really nice describe. Really awesome. Could you also write one on Flex 4 component life cycle.

    Regards,
    Ashish Mishra

    VA:F [1.9.8_1114]
    Rating: 1.3/5 (3 votes cast)
  5. numan
    August 3rd, 2011 at 07:03 | #5

    great tutorial! helped demystify stuff for me. thanks and keep it coming!

    VA:F [1.9.8_1114]
    Rating: 5.0/5 (1 vote cast)
  6. gopi
    October 19th, 2011 at 05:03 | #6

    Its really worth to read.. gr8 explanation. it helped lot…

    VA:F [1.9.8_1114]
    Rating: 5.0/5 (1 vote cast)
  7. November 8th, 2011 at 12:59 | #7

    It’s really good.It help me a lot

    VA:F [1.9.8_1114]
    Rating: 5.0/5 (1 vote cast)
  8. Amit Tamse
    December 7th, 2011 at 11:27 | #8

    Its really good blog. Nice explanation

    VA:F [1.9.8_1114]
    Rating: 5.0/5 (1 vote cast)
  1. March 19th, 2011 at 23:59 | #1
  2. November 14th, 2011 at 19:38 | #2
Put ActionScript or MXML code snippets between tags: [as3][/as3]
You can also resize the comment area by dragging the bottom right corner.