Sunday, April 18, 2010

Why ActionScript and why not MXML. [ActionScript vs MXML]

1. You cannot add new childrens to complex components via MXML, you have to use ActionScript.

2. MXML encourages inline event listners and curly brace bindings. These event listeners and bindings are difficult to remove.

3. Compiler creates a lot of ComponentDescriptors. Components created from ComponentDescriptors create a lot of objects (Function, Object, ChildDescriptor etc). This can affect the overall performance, memory and load time.

4. All controls/components declared in mxml have a public scope. This breaks encapsulation.

5. MXML inline event listeners are always in bubble phase. This is really not a problem but just worth mentioning.

6. MXML components create a lot of references e.g. component descriptors, watchers, binding and function objects etc. These references are like noise and make it difficult to analyze memory leaks.

7. In MXML, you cannot use a component as a Singleton.

8. MXML components create a large object graph (component descriptors, bindings, watchers, watcherSetupUtils etc etc) which can be difficult for the GC to analyze. ActionScript components on the other hand are light weight (compared to MXML components) with a lot fewer references. This makes it easy for the GC to analyze the object graph and garbage collect them.
Remember, Flash GC is not forward progressing and incremental. If a large object graph cannot be analyzed in the limited time the GC gets, all objects in the object graph will start leaking.

9. There are many things not possible in an MXML component. I once tried setting a "this" on a static variable in an MXML component but kept getting an error. I could have easily done that in an ActionScript components constructor.

10. Try "override mx_internal someBaseClassMethod()" in an MXML component. I have done a test project where I extended the Canvas component and created an MXML and ActionScript component to test the "override mx_internal someBaseClassMethod()". Got errors in the MXML component (and eventually could not do it) but Actionscript component worked like a charm.

11. The compiler creates a lot of files/ additional functions, addtional getter/setters, private variables for every MXML class. className-generated.as, className-watcherSetupUtils and a few more files that I don't remember but can look it up. Every inline event listener is wrapped in a compiler-generated public function.

12. CTRL + SHIFT + O in Flex/Flash Builder removes unwanted imports in .as files. This doesn't work in .mxml files.

13. Try using a custom component that extends UIComponent in mxml and add a child to it ...

class MyUIComponent extends UIComponent { ... }
[now use the above custom component in an mxml application]




The application will not compile and it will throw an error. But this should be very easy to do it ActionScript.

14. Cannot conditionally create objects. This is a very important for performance and memory. Let me explain this with an example. I have a component which displays a warning image under certain conditions. If the condition is not met, why create the Image component and display nothing. Creating less objects will speed up loading time for the view, consume less memory and the overall ui will be a lot more responsive and snappier.

15. ... and I am sure many more drawbacks that I have not encountered.

The positive side of MXML components is declarative programming/ inline bindings/ inline events but they all come with a cost.

I literally live in the world of Flex profiling and I notice thousands of objects (bindings etc) that could have been avoided and some memory and performance could have been gained. But alas, not every Flex programmer loves coding in ActionScript.

Feel free to add your experiences and views regarding ActionScript/MXML in the comments.

Labels: , ,

Monday, April 5, 2010

Carefull with Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE

Use these events very carefully because the Flex Container class re-parents children. This is done during resizing. The Container class which is the super class of HBox/VBox etc tries to create scrollbars and in the process creates something called contentPane. Here is the method in the Container class that does the reparenting...


mx_internal function createContentPane():void {
....
....
....
for (var i:int = 0; i < n; i++)
{
// use super because contentPane now exists and messes up getChildAt();
var child:IUIComponent =
IUIComponent(super.getChildAt(_firstChildIndex));
newPane.addChild(DisplayObject(child));
child.parentChanged(newPane);
_numChildren--; // required
}
}


The above code results in firing the Event.REMOVED_FROM_STAGE and Event.ADDED_TO_STAGE. This can be dangerous if you are relying on these events to do something critical like destroying the view or its associated controllers etc. I have also seen implementations where a view listens to these methods and calls a dispose method to remove listeners, unwatch bindings and cleanup memory.

In short, these events become unreliable.

Labels: ,

Sunday, April 4, 2010

DoubleClick the column seprator in AdvancedDataGrid and resize columns like Excel

Resizing not exactly like Excel, but double clickable and some intelligent resizing. Check the Flex 4 demo.

Click here for a demo
View source: Right click demo, or download from here

Note:The last 3 columns have the preferredWidth set to 40. The first 3 columns are dynamic.

Explanation:
We extend the AdvancedDataGridColumn and added a new property called preferredWidth. We do this so that some columns which show boolean values or simple icons do not get more width than they need. This also doesn't deprive other columns of the much needed width to display correctly.

You will need to set the AdvancedDataGrid's doubleClickEnabled=true. The AdvancedDataGrid class and the super class listen to the double click event and the method name is "mouseDoubleClickHandler". So override the method "mouseDoubleClickHandler" and after calling super.mouseDoubleClickHandler(), plugin the autoResize code.

How to know that the separator was clicked?

if (event.target && event.target is UIComponent) {
var uiComp:UIComponent = event.target as UIComponent;
if (uiComp.parent && uiComp.parent.name == "header") {
autoResizeColumns();
}
}

All separators are children of a UIComponent and the name given to this UIComponent is "header" (by the AdvancedDataGrid). How do I know this ... there is a wealth of information if you enjoy looking at the Flex SDK source code. You learn a lot too and that is how I have learnt my component development and a lot of other tricks. "There are 2 types of Flex developers, one who don't enjoy reading other peoples code and one who enjoy reading other peoples code. It is better to quickly switch to the latter side." Sorry for that funny line, it is a dialogue from a Bollywood movie which goes like ... THERE ARE 2 TYPES OF PEOPLE IN THIS WORLD, ONE WHO blah blah and OTHER WHO blah blah. THE SOONER YOU GET TO THE OTHER SIDE, THE BETTER IT IS FOR YOU. Start reading/understanding the SDK source code, you will learn a lot of nice tricks and will also help you extend the base controls.

In the above code we check if the double click happened on a component whose parent's name was "header", if yes, the separator was clicked hence call the autoResizeColumns() method. There is not much in this method except that it takes into account the preferredWidth and decides what the AdvancedDataGridColumn width should be. Notice the use of the "displayableColumns" variable. This is something I found when exploring the AdvancedDataGrid. It contains the list of visible AdvancedDataGridColumns.

autoResizeColumns(): The logic and algorithm for width calculation really depends on your needs. It could be equal divide or as per the itemRenderer's measuredWidth or whatever your project needs are.

I tried implementing the resizing based on the measuredWidth logic. Unfortunately, some components do not report the measuredWidth correctly and the experiment failed. If you do want to play with it than here is an important information you will want to know. The itemRenderer instances are all available in the listItems collection. The listItems collection contains array objects. One array for each row. Each row array contains instances of the itemRenderer for all the visible columns. Is this all documented? Maybe! I found it during one of the deep dive session.

Play with it, implement it in your projects and wow your managers and project managers.

Labels: , , ,

Saturday, March 27, 2010

An AdvancedDataGrid with a built-in Column Selector

The AdvancedDataGrid reminds me of Excel but the fact is that it is no where close to what Excel does. The AdvancedDataGrid is a complex component with many different classes (AdvancedDataGrid, AdvancedDataGridColumn, AdvancedDataGridHeaderRenderer and many other) working together to render the grid. Not much effort has been put into advancing the AdvancedDataGrid and make it behave a little bit like Excel. Here is my feeble effort to make it a little bit more advanced.

I have seen the need to show/hide column of a grid in a few projects. The first time, I put a PopupDropdown with a list of columns having a checkbox next to it. The second time we had a similar need and one of my colleague challenged me with a built-in column selector inside the AdvancedDataGrid. In the end, what he proposed is what he got. The screenshot below shows the end result.



Click here for a demo
View source: Right click demo, or download from here

[Incomplete:explanation coming soon]

Labels: , ,

Friday, March 26, 2010

ComboBox memory leak

A ComboBox is a very commonly used control. It looks very harmless but can be very dangerous as it leaks memory.

A Flex bug has been filed and here is the link. But Adobe is lazy and they will do nothing other than sit over the bug.

You can download the source code from here for the below example if you would like to profile and see the memory leak in the profiler.

The demo contains 2 custom components.
CustomComponent1: is a very simple component which contains a TextInput and CustomComponent2.
CustomComponent2: is also a very simple component. It contains a dropdown.
A button in the application removes the CustomComponent1. CustomComponent1 being a hello world type component, it should be garbage collected once it is removed from the displayList. But it does not. Check the image below.


The above screenshot is taken from the profiler. CustomComponent2 leaks and the references have been expanded. One of object referencing it is mx.controls.List. But List is really not the child of CustomComponent2 than why would it reference it? The document is CustomComponent2 hence all children and grand children of CustomComponent2 have the document property set to CustomComponent2 unless there is another document that contains the grand children. The List's document property is CustomComponent2. Finally, the List is being referenced by SystemManager as highlighted in red.

But why is the List referenced by the SystemManager? A ComboBox contains a dropdown which is really a List. The dropdown though referenced by the ComboBox is really not the child of the ComboBox. It is a Popup. Check ComboBox.as and you will see a lot of PopupManager.addPopup(_dropdown) lines. It is made to look as if it is dropping out of the ComboBox. A popup's parent is always a systemManager. That explains the relation between ComboBox -> dropdown (List) -> SystemManager.

Check this comment in the ComboBox.as ...

private function removedFromStageHandler(event:Event):void
{
// Ensure we've unregistered ourselves from PopupManager, else
// we'll be leaked.
destroyDropdown();
}

... despite knowing about the potential leak, the leak still exists.

Solution:
Extend ComboBox and add this method ...


//add event listender in the constructor
addEventListener(Event.REMOVED_FROM_STAGE, this_onRemovedFromStage);

...
...

protected function this_onRemovedFromStage(evt:Event):void {
if (mx_internal::hasDropdown()) {
var dd:ListBase = dropdown;
if (dd && dd.parent) {
dd.parent.removeChild(dd);
}
}
}


Now imagine an application which contains ...
CustomComponent1 which contains
    |____CustomComponent2 which contains
        |____CustomComponent3 which contains
            |____ComboBox which references
                |____dropdown whose parent is
                    |____SystemManager

The dropdown leaks hence the entire chain of components will leak. This bug has been reported to Adobe but I am not sure why they haven't fixed it.

Labels: , ,

Wednesday, March 24, 2010

Flex Builder: What is my current workspace?

I work on a project that has multiple branches. Sometimes I am working on branch1 and sometimes on branch2. I would like Flex/Flash Builder to display the branch I am working on. A quick search on the net and I found the solution.

Open the FlexBuilder.ini file. Add this to the first line "-showlocation". This is what my FlexBuilder.ini file looks like...

-showlocation
-vmargs
-Xms128m
-Xmx512m
-XX:MaxPermSize=256m
-XX:PermSize=64m
-Djava.net.preferIPv4Stack=true

This what my FlexBuilder title looked like before adding showLocation

...and this what it looks like after adding showLocation.


The FlexBuilder.ini file should be in your FB install folder.

Labels:

Tuesday, March 23, 2010

TextInput as Label

I have often seen the need to make a TextInput behave like a Label. This need generally comes from a form which is in editable/read only mode.

Here is a TextInput mxml that shows you what properties and styles need to change to make the TextInput behave like a Text or Label.


<com:TextInput text="prashant"
backgroundAlpha="0" borderStyle="none"
editable="false" focusThickness="0"/>

I remember seeing an example posted by someone where a Label was included inside the TextInput and then with the help of some black magic, the Label is displayed and UITextField inside the TextInput is made invisible. I liked my solution more for the simple fact that it does not have to add any extra uicomponents and works on the same object which is the UITextField.

Click here for a demo
View source: Right click demo, or download from here

The demo extends the TextInput class and adds a new property called "useAsLabel".

Labels: , ,