The purpose of the suspend() function is to freeze and store the current state of the component. It's partner function resume() starts any frozen processes back up and returns the component to the state it was in. Both functions help to manage state and memory and are related to the destroy() function I proposed in my last post.

The Problem
Removing a DisplayObject from the display list does not cause it to stop running. This presents a unique problem for Flash developers who are used to using the timeline to control the state of their application.

Let's say you have a Flash microsite that contains three or four pages. In a typical AS1 or AS2 project each page would live inside its own MovieClip. An instance of each page would then be placed (with a corresponding label) on the main timeline. The gotoAndPlay() command is then used to navigate between pages.

This pattern works fine in AS1 and AS2 because the AVM1 compiler destroys any MovieClip that is no longer on the stage. Consider then how this pattern would work in AVM2:

The first thing the user will see is the 'home' page movie. If let's say they navigate to the companies page; the timeline will shift to show the 'companies' page movie and the 'home' page is removed from the display list. But, as we've already established, removing a DisplayObject from the display list does not cause it to stop running.

Moving from page to page will instantiate and remove (but not destroy) all the other page movies. If the state of each page is not properly managed then a number of problems present themselves:

  • If all the pages in your site are running concurrently then you're likely to have a problem with performance.
  • If one or more of your pages contain elements that are heavy on the processor (e.g. large videos, layered effects or real time 3D) then these will continue to be processed even though the page is not visible.
  • If any of your pages play audio or video that contains an audio track, then you will continue to hear it play even though you can no longer see the page.

The Solution
The simple answer is to avoid using the timeline to manage state in AS3. In the new world order you would have a PageManager class that creates and destroys pages as required. The thing is, Flash developers (as opposed to back-end or even Flex developers) are very 'visual people'. They like to see the objects they are managing and the timeline is a familiar way of doing that.

If every component already has suspend() and resume() functionality built in then we can automate some of the state management process by listening for stage addition and removal events. When an REMOVED_FROM_STAGE event is detected we call suspend() and when an ADDED_TO_STAGE event is detected we call resume(). Early versions of the player did not support these events so if you need to target an early version of the player then you may wish to use Senocular's code for detecting stage events.

Update: Both functions are public. This allows a component to be suspended and resumed as required. For this reason I have added a read-only property isSuspended to indicate whether the component is suspended or not.

The Benefits
Being able to suspend and resume your components will improve the overall performance of your application. Correctly implemented it should avoid the problem of components continuing to run after being removed from the display list.

The Code

package org.computus.core
{
  import flash.events.Event;
  import flash.display.MovieClip;

  public class AbstractComponent extends MovieClip
  {
    // ------------------------------------------
    // PROPERTIES

    // isSuspended starts as true as the component is off stage
    protected var _isSuspended:Boolean = true; 

    // ------------------------------------------
    // CONSTRUCTOR
    public function AbstractComponent():void
    {
      super();
      init();

      if (stage)
      {
        resume()
      }
    }

    // ------------------------------------------
    // MEMORY MANAGEMENT
    protected function init():void 
    {
      // concrete classes that override this function should call super.init()
      // n.b. These events require Flash Player 9.0.28.0
      addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
      addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0, true);

      // initialise component here.
    }

    public function destroy():void
    {
      // concrete classes that override this function should call super.destroy()
      // remove all listeners here suspend();
      removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
      removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
    }

    public function suspend():void
    {
      // concrete classes that override this function should call super.suspend()
      // suspend all processes
      _isSuspended = true;
    }

    public function resume():void
    {
      // concrete classes that override this function should call super.resume()
      // resume all processes
      _isSuspended = false;
    }

    public function get isSuspended():Boolean
    {
      return _isSuspended;
    }

    // ------------------------------------------
    // EVENTS
    private function onAddedToStage(e:Event):void
    {
      resume()
    }
    private function onRemovedFromStage(e:Event):void
    {
       suspend()
    }
  }
}