Archive for June, 2009

Architecting a Flex 4 component

One of the biggest improvement in the new version of Flex SDK is the methodology by which you design and implement a component. If in Flex 3 you would probably mix skin mxml code with actionscript logic code, a Flex 4 component has 2 main parts: skin and model/controller. Each of these represent a different object managed by the component itself. The skin part contains mostly the mxml pieces defining the visual elements of the component while the model component handles all the logics and controlling. At the core of a component are the states, which were in Flex 3 also but are way better handled and improved in 4. When starting designing a component, it’s states need to be ruled out and logic that handles them. This is achieved on the actionscript side of code, declaring variables by which states are popped into the stage. Lets get this on one example. Suppose we need a contact form for customers to get feedback. Thinking of it’s functionality it’s clearly that it needs 3 states: default state where initial text is displayed, input state where client has controls for inserting code and final state where thank you and such message is displayed.
Let’s call this component ContactForm, the class definition would then look like this:

1
2
3
4
5
[SkinState( "normal" )]
[SkinState( "input" )]
[SkinState( "final" )]

public class ContactForm extends SkinnableComponent

SkinState metadata specifies what states the skinnable component has, linking them with the states declared in the skin class that looks like this:

1
2
3
4
5
<s:states>
    <mx:State name="normal"/>
    <mx:State name="input"/>
    <mx:State name="final"/>
</s:states>

The next thing would be to declare in the model class all the logic-enabled controls. Logic enabled controls refer to controls that play a role in a component’s lifecycle, such as buttons that trigger state change, text control that offer text for submitting and so on. So lets go ahead and add the skin parts to our component:

1
2
3
4
5
6
7
8
[SkinPart( required="true" )]
public var openInputButton:Button;
   
[SkinPart( required="true")]
public var inputTextArea:TextArea;
   
[SkinPart( required="true" )]
public var submitButton:Button;

Ok. Once we have all the visual elements defined it’s time to work on the logic part of it. Adding boolean properties for ruling out in what state the component should be is a good practice. For the contact form demo component we have only 2, isInput which should tell the component that currentSkinState should be input, and isFinal which reflects the final state where thank you message is displayed. So two new lines to the class:

1
2
private var isInput:Boolean;
private var isFinal:Boolean;

The main skinning method that is overridden is “getCurrentSkinState” which gets called when skin state is invalidated thru “invalidateSkinState”. Basically this method returns the current skin based on the logic properties. Pretty straightforward:

1
2
3
4
5
6
7
8
override protected function getCurrentSkinState():String
{
    if( isFinal )
        return "final";
    else if( isInput )
        return "input";
    else return super.getCurrentSkinState();
}

Now skin parts need a little handling in terms of adding/removing event listeners and other data related computations. These are very handy when designing the component for memory optimization as you can remove event listeners and decouple the component from data area of an application (more on this here). Here they are:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
override protected function partAdded( partName:String, instance:Object ):void
{
    super.partAdded( partName, instance );
       
    if( instance == openInputButton )
        openInputButton.addEventListener( MouseEvent.CLICK, openInputButtonClickHandler );
    else if( instance == submitButton )
        submitButton.addEventListener( MouseEvent.CLICK, submitButtonClickHandler );
}
   
override protected function partRemoved( partName:String, instance:Object ):void
{
    super.partRemoved( partName, instance );
       
    if( instance == openInputButton )
        openInputButton.removeEventListener( MouseEvent.CLICK, openInputButtonClickHandler );
}

And here are the event handlers that rule out the transitions between component states:

1
2
3
4
5
6
7
8
9
10
11
12
private function openInputButtonClickHandler( e:MouseEvent ):void
{
    isInput = true;
    invalidateSkinState();
}
   
private function submitButtonClickHandler( e:MouseEvent ):void
{
    isInput = false;
    isFinal = true;
    invalidateSkinState();
}

What happens under the hood is that when “invalidateSkinState()” gets called, framework will eventually call “getCurrentSkinState” and based on the state received it invalidates the skin and update based on that value.
These is pretty much it on the model class. It should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package com.ayone.examples.contactForm.ui
{

import flash.events.MouseEvent;

import spark.components.Button;
import spark.components.TextArea;
import spark.components.supportClasses.SkinnableComponent;

[SkinState( "normal" )]

[SkinState( "input" )]

[SkinState( "final" )]

public class ContactForm extends SkinnableComponent
{
    //------------------------------------------------------------
    //
    // Properties
    //
    //------------------------------------------------------------
   
    //------------------------------
    // Skin Parts
    //------------------------------
   
    [SkinPart( required="true" )]
    public var openInputButton:Button;
   
    [SkinPart( required="true")]
    public var inputTextArea:TextArea;
   
    [SkinPart( required="true" )]
    public var submitButton:Button;
   
    //------------------------------
    // State Related Logic
    //------------------------------
   
    private var isInput:Boolean;
   
    private var isFinal:Boolean;
   
    //------------------------------------------------------------
    //
    // Methods
    //
    //------------------------------------------------------------
   
    //------------------------------------------------------------
    // Skinning
    //------------------------------------------------------------
   
    override protected function getCurrentSkinState():String
    {
        if( isFinal )
            return "final";
        else if( isInput )
            return "input";
        else return super.getCurrentSkinState();
    }
   
    override protected function partAdded( partName:String, instance:Object ):void
    {
        super.partAdded( partName, instance );
       
        if( instance == openInputButton )
            openInputButton.addEventListener( MouseEvent.CLICK, openInputButtonClickHandler );
        else if( instance == submitButton )
            submitButton.addEventListener( MouseEvent.CLICK, submitButtonClickHandler );
    }
   
    override protected function partRemoved( partName:String, instance:Object ):void
    {
        super.partRemoved( partName, instance );
       
        if( instance == openInputButton )
            openInputButton.removeEventListener( MouseEvent.CLICK, openInputButtonClickHandler );
    }
   
    //------------------------------------------------------------
    // Event Handling
    //------------------------------------------------------------
   
    private function openInputButtonClickHandler( e:MouseEvent ):void
    {
        isInput = true;
        invalidateSkinState();
    }
   
    private function submitButtonClickHandler( e:MouseEvent ):void
    {
        isInput = false;
        isFinal = true;
        invalidateSkinState();
    }
}

}

But wait there’s more. We still didn’t rule out the skin class. First thing to be added is the state declaration which links the states from the main class to skin class:

1
2
3
4
5
<s:states>
    <mx:State name="normal"/>
    <mx:State name="input"/>
    <mx:State name="final"/>
</s:states>

Nothing new here. The newly addition to the state management in Flex 4 is the state-related properties that enables one to set let’s say height for a skin depending on what state it is. Something like this:

1
2
3
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/halo"
    width="400" height="30" height.input="200">

Notice the two height declaration that are translated like this: height in “input” state should be 200 and 30 in any other state. All we need to do now is declaring the visual elements that forms the skin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<s:Group includeIn="normal" width="100%" height="100%">
    <s:layout>
        <s:HorizontalLayout verticalAlign="middle"/>
    </s:layout>
    <s:SimpleText text="Click here to get in touch with us"/>
    <s:Button label="Contact" id="openInputButton"/>   
</s:Group>

<s:Group includeIn="input" width="100%" height="100%">
    <s:layout>
        <s:VerticalLayout horizontalAlign="right"/>
    </s:layout>
    <s:TextArea id="inputTextArea" width="100%"/>
    <s:Button id="submitButton" label="Send"/>
</s:Group>

<s:SimpleText includeIn="final" text="Thank you for contacting us"/>

Note that actual skin parts declared in main class don’t have any property related to state management. Instead those are wrapped in Group components and those are state-enabled thru “includeIn” property. “includeIn” basically states that a visual element should be present only in that specific state (or state group, more on that in a future post). And this is the beauty of it, once the main class has defined the skin parts that it requires for a functional component, you can mix and play with the skin as much as you want.

That would be pretty much it. You can download the source code from here.

Flex 4 Memory Optimizations

One of the most problematic and discussed area in Flex development is garbage collection and memory optimization. Even with new tools added to Flex Builder (now Flash Builder) like Profiling, struggling to obtain a memory clear application is not easy. One of the methods is the undocumented hack with LocalConnection
where you connect two of them and do nothing at all after.

1
2
3
4
5
6
7
8
9
try
{
    var lc1:LocalConnection = new LocalConnection();
    var lc2:LocalConnection = new LocalConnection();

    lc1.connect( "gcConnection" );
    lc2.connect( "gcConnection" );
}
catch (e:Error) {}

This will trigger the internal garbage collection and clear everything that is decoupled from application.
But doing just this doesn’t mean your code will work as expected. The whole architecture needs to be ruled out with GC in mind and here is where the Flex 4 framework came in big help. Offering methods like partAdded() and partRemoved() to skinnable components is a path that assures every listener added to skin parts is removed in partRemoved(). This means if you show/hide some parts without removing the whole component from stage, all you need to do is call partRemoved() and partAdded() on the part you need to garbage collect. Make sure you call theese methods only if the part exists first as the first partAdded() is called from withing the component. This technique applies mostly to DataGroup where itemRenderers are used and bound with data that might have a longer lifespan than the renderer itself. Another critical time where some extra logic for memory should be added is when renderer is removed from stage, aka when “data” property is set to null. Here is where you might want to call detachSkin() to make sure all parts are decouples from the external resources and available for garbage collection.

Another handy method relates to images, components that are very problematic when comes to garbage collecting, where there is a new functionality inserted in Flash Player 10. SWFLoader has an unloadAndStop method that takes “invokeGarbageCollector” as a param which eventually result in calling “unloadAndStop” method available in Flash Player 10.

Working on a data intensive project in Flex 4 i’ve noticed that the framework better supports memory optimization that it did before so kudos for SDK guys. Will get back here with any interesting idea related to garbage collector in Flex 4 and where is the next critical place where you need to do extra work for it.

We are back in the blogosphere, with a little extra

You might know the person who is writing here from the old blog at http://blogs.eyepartner.com/adrian or from the twitter page http://twitter.com/aadrian. Same as before, this blog will cover RIA seen from the Adobe’s area, using AIR/Flex 4/Catalyst along with topics of SpringActionscript, Cairngorm and BlazeDS. The newly addition is Andrei, who will cover the Java part, posting about SpringSource related projects, SpringDM server, LCDS, BlazeDS, Hibernate and such topics. Together we form the base start of Ayone, a software company focused on Rich Internet Applications, offering solutions and consultancy in this area. If you want to contact just drop us a line and we’ll get in touch in no time.
That should be it for the introductory post, stay tuned for articles and news about what we do and how we do it.