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: , , ,