Enterprise APP

ItemRenderer 메모리 이슈 본문

Componet Directory/DataGrid/Advanced/List

ItemRenderer 메모리 이슈

로드스타 2010. 9. 20. 17:05
이미지 사용지 static class에서 불러와 사용
Image 대신 BitmapImage 사용

2010
03.05

Now, more than ever, is the time to cease adding components to itemrenderers on the whim and start really thinking about what should be included; what  the minimalist components are that will do the job right.

If you are interested in performance and memory issues in AS3 I would highly recommend reading the following:

Fast Integer math:

On to some more Item Renderer specific guidelines

____________________________________________________________________________________

Extract the common visual elements

It is a good starting point to extract all the common visual items from the itemrenderers. Particularly useful if you use ‘on mouseOver show’ controls as illustrated below:
As you can see the controls are only required when a user rolls over an Item and therefore these controls (play && download buttons) only need to be instantiated once == Yippie much better performance + lower memory usage.

How to create the above TileList example:

Each Item Renderer will only have the Image and associated text (in this case album/artist name) and the controls will be created and added to the parent item (List – Gumbo, TileList-Flex3 etc…), we can then listen to the ITEM_ROLL_OUT && ITEM_ROLL_OVER events and position/show our controls accordingly using the X & Y coordinates of the event.target which will be each Item in the list.

The Flash plugin is required to view this object.

View Source

As you scroll through the above list you can see the RED square following the items in the list (I am using tweener to follow the list but this can be popped up in the correct position instantly), it is not so apparently beneficial with so little items though start to increase the numbers and watch the render time lower and physical memory usage drop off.

Using images inside an itemrenderer

The approach to take is dependant on if the images are static or dynamic/remote, I’ll cover remote images in detail though by doing this it should answer any query’s you may have about static ones.

Remote/Dynamic Images

The easiest and heavy approach is to simply use the “Image” component (mx.controls.Image), the Image is great to use outside of an itemrender though you when you look into the image class you realize it extends SWFLoader (hence why all the loading is done for you), this can be extremely expensive in memory.

Solution: Use the new BitmapImage (spark.primitives.BitmapImage) component in junction with a static loader class (Simple static class external where all loading is handled for you).

You could also make use of current ‘Bulk Loaders‘ such as http://code.google.com/p/bulk-loader/, It is almost always better to build these types of things yourself though (in terms of performance), if anybody needs some more details please just Twitter/Hola/Email/Touch-Me

Text

When building Flex 4 Applications you should use the Spark controls where possible, the spark controls are lighter and have better font embedding, if you are NOT using “Right to Left” text you should NOT be using RichText (made the mistake early on).

The spark label (spark.components.Label) field is light enough to use with large data providers

nothing more to say here….

Events

As much as we would like ‘Events’ not to be used inside item renderers we simply can not survive without them, one of the key things to remember is that if we do not REMOVE/DISPOSE the EventListeners when the itemrenderer is destroyed (removed from stage, data changed in list) the flash Garbage Collector may not be able to completely destroy it.

/**
* Dispose Item
* @public
* */
public function dispose( ):void
{
	removeEventListeners( );
	//Destroy Children && Elements
}
 
/**
* Activate Deactivate Component
* @private
* */
private function activateDeactivate( evt:Event ):void
{
	switch( evt.type )
	{
		case Event.ADDED_TO_STAGE:
			addEventListeners( );
			break;
		case Event.REMOVED_FROM_STAGE:
			dispose( );
			break;
	}
}
/**
* Add Event Listeners
* @private
* */
private function addEventListeners( ):void
{
	addEventListener( Event.REMOVED_FROM_STAGE, activateDeactivate, false, 0, true );
}
/**
* Remove Event Listeners
* @private
* */
private function removeEventListeners( ):void
{
	removeEventListener( Event.REMOVED_FROM_STAGE, activateDeactiveHandler, false );
 
       //Remove all other event listeners here
}

This example shows how you could handle adding and removing events from the renderer, when the renderer is destroyed it is removed from the stage causing the Event.REMOVED_FROM_STAGE to dispatch, we can not remove all events and make sure all children/elements are destroyed correctly (Garbage Collector == Happy).

Typical I create an interface (IDisposable) and implement this with function destroy( ):void.

It would be worth noting this applies to any custom component, it is good to clean up after yourself (remember wifey giving you a hard time about the dishes!)

Timers VS EnterFrame

I have no idea why somebody would need either of these in Itemrenderer’s, perhaps some Photoshop ninja requires an animated swirl every couple of seconds to tempt users to interact.  I would personally  fight this off as much as possible, though if  unavoidable it is good to understand the differences between these two approaches.

The facts:

  1. Timers are more expensive than the EnterFrame event (more CPU intensive).
  2. Timers are more accurate than the EnterFrame as the EnterFrame event relies heavily on a users CPU (a slower CPU == less times EnterFrame fired) though it is not true that the Timer is 100% accurate itself, more information can be found here http://www.bit-101.com/blog/?p=910

This is getting long and bloated which is exactly what I wanted to avoid, maybe this should be done in parts (see you in part II)

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Blogplay

4 comments so far

Add Your Comment
  1. Hi. Thanks for writing this. It is very helpful. We have exactly the problem you describe here which is an ItemRenderer that won’t garbage collect. It has an EventListener, and I have been trying to figure out how I can know when to remove the EventListener so that these very expensive ItemRenderers will get garbage collected.

    I have a couple of questions on your example. The REMOVED_FROM_STAGE fires even when a window is minimized, right? So when the window re-opens you want your renderers to be there. How do you make sure to “clean the dishes” so to speak only when the window is all the way gone?

    And this line:
    removeEventListener( Event.REMOVED_FROM_STAGE, activateDeactiveHandler, false );
    You don’t have a function called activateDeactivateHandler, just an activateDeactivate. Is this supposed to be removing the same event listener that was added in this line?
    addEventListener( Event.REMOVED_FROM_STAGE, activateDeactivate, false, 0, true );

    And then at least in the version of Flex I am using, useCapture defaults to false. Is there any reason to pass false in that argument since it will default to that anyway?

    Like or Dislike: Thumb up 0 Thumb down 0

  2. Also I don’t see how it gets to this line:

    private function activateDeactivate( evt:Event ):void
    {
    switch( evt.type )
    {
    case Event.ADDED_TO_STAGE:
    addEventListeners( );

    I don’t see any listener for ADDED_TO_STAGE,activateDeactivate.

    I wonder if there is more code that I just can’t see.

    Like or Dislike: Thumb up 0 Thumb down 0

  3. Hi Lisa

    Thanks for taking the time to point out some of my copy/paste typos, a bit has changed since I originally wrote this article (Flex beta to full version) and some of my original findings. The part section you refer to about “Events” is to create a system for each renderer item to have an inactive/active state, item renderers use the object pooling pattern and hence I wanted to show you could take advantage of the native “Event.ADDED_TO_STAGE && Event.REMOVED_FROM_STAGE” listener to set up each time a renderer is created or taken from the pool (reused item).

    On your questions about the listeners in the example:

    “activateDeactivate” vs. “activateDeactiveHandler”, I made a bit of a booboo copy pasting some of the stuff from different projects in my examples, in this example everything should be pointing to “activatDeactivate”.

    useCapture property, the addEventListener property I was illustrating was the “useWeakReference” which is after the useCapture property, I know I also used it in the removeEventListener even if it’s default is “false” (habit of seeing all property values really), you can ignore this and leave it out.

    the method activateDeactivate handler requires a listener for Event.ADDED_TO_STAGE, this can be added in the childrenCreated Handler:

    override protected function childrenCreated( evt:FlexEvent ) : void
    {
    super.childrenCreated( evt );
    addEventListener( Event.ADDED_TO_STAGE, activateDeactivate, false, 0, true );
    }

    The main premise of the example on events was more to show that when a item is not in view (added to stage) we can remove any references it may have (to static class, image cache, listeners etc…) so when garbage collection does occure we know flash will remove these items.

    If you would like to send me the item renderer you are having trouble with I would be happy to digg through and see if anything might prevent flash from not destroying it? (tyrone_neill@yahoo.co.uk)

    Like or Dislike: Thumb up 0 Thumb down 0

  4. but how about the removal of the ADDED_TO_STAGE event you added in your childrenCreated ovverride?
    is it necessary as well?

    Like or Dislike: Thumb up 0 Thumb down 0

Comments