Mastering the Tao of Personal Computing

going to see the first office for Obecto 2 hrs ago

Flex Application Bootstrapping - Totally Custom Preloader

Jul 6th 2008
4 Comments
respond
trackback

I’m not going to define and discuss what a RIA is, but I rather stress here on one very important aspect of rich internet applications and in particularly saving time. Time is money (or opportunities for pleasures and entertainment), having more time is having more money (or opportunities for pleasures and entertainment), so that’s where ‘rich’ comes from. Flex applications that take eternity to load and don’t offer you something enough valuable as compensation for your patience are waiting in vain, just as in the song. This is one reason why rich internet applications come with very specific requirements and tight restrictions about what’s loaded and when. Chunks of functionality and data are dispersed in time and are ordered by their purpose in the concrete application.

The Specific Requirements

BombaySapphire.com - that’s a typical example. The requirements for the application loading can be summarized in the following diagram, where the gray bars depict the life-span of the different visual phases of the Bombay application, and the red ones are showing the time needed for loading the various parts and data, comprising the application. In case you wonder the LDA abbreviation stands for Legal Drinking Age validator.
Schematic Application Timeline
Instead of having all the gray bars be part of a monolithic whole, the guys from Adobe had provided us with the 2-frame Flex application model, where the first frame of the application is solely devoted on the preloader, while the second is available for the actual application.
Flex Application Two Frames Model
In the rest of the article I’m going to describe how to setup not just a skinned preloader, but a totally custom preloader. For those interested in just skinning the default Flex preloader - mx.preloaders.DownloadProgressBar - you should continue with Jesse Warden’s great article on the topic. But before proceeding with the implementation of the IPreloaderDisplay interface, we need to take a look at the Flex SystemManager and its responsibilities and also to get familiar with some events that the Preloader dispatches.

The Flex SystemManager

1. The SystemManager is the first display class created within a Flex application.
2. It is responsible for creating the Preloader and handling its events.
3. The SystemManager is also responsible for advancing the Flex application to the second frame once the application SWF is fully downloaded.

Besides these responsibilities directly related to the Preloader, the SystemManager is also responsible for:
4. Managing an application window and sending events if the size of the window changes.
5. Maintain tooltips, cursors and popup windows.
6. Expressly trapping the keyboard and mouse activity.
7. Some other stuff, I hadn’t time to read about.

Preloader’s most important events

1. ProgressEvent.PROGRESS
This is a typical progress event - it indicates what part of the application is loaded.
2. FlexEvent.INIT_PROGRESS
This event is dispatched when the Flex application completes an initialization phase - once the application SWF is fully loaded, the SystemManager advances the playhead to the next frame. Usually you’ll need to control the specific moment when the application advances to the second frame in the 2-frame model. In the samples below I’ll show one ways to achieve this - basically you need to capture the event, store it as pending and suspend the succeeding dispatches of these type of events - later you should re-dispatch the stored pending event.
3. FlexEvent.INIT_COMPLETE
This is the last event the Preloader dispatches. Next the Preloader should dispatch Event.COMPLETE indicating that the SystemManager is ready to remove the Preloader and to add the application to the display list.

Implementing the IPreloaderDisplay interface

I think this is the right place in the article to show you a sample implementation of the IPreloaderDisplay interface. Let’s start with the trivial part of the interface - some background and stage related properties. In the IPreloaderDisplay these are defined as get and set functions, which allows me to implement them by just declaring the corresponding public variables.

public var backgroundAlpha : Number;
public var backgroundColor : uint;
public var backgroundImage : Object;
public var backgroundSize : String;
public var stageHeight : Number;
public var stageWidth : Number;

The most convenient place for adding listeners for the events dispatched by the Preloader-component is in the set preloader() method of the interface.

private var _preloader : Sprite;
public function set preloader(value : Sprite) : void
{
    _preloader = value;

    _preloader.addEventListener(ProgressEvent.PROGRESS, onPreloaderProgress);
    _preloader.addEventListener(FlexEvent.INIT_PROGRESS, onPreloaderInitProgress,
        false, int.MAX_VALUE);
    _preloader.addEventListener(FlexEvent.INIT_COMPLETE, onPreloaderComplete);
}

The last method we must implement is the initialize() method. This method is called right after the Preloader is added as a child. This is the entry point where you can configure your preloader. For example I use this method to draw the background, filling it with a custom pattern or just adding visual components as children to this totally custom preloader.

public function initialize() : void
{
    // e.g. draw a background
    ...
    // or add visual children to the totally custom preloader
    ...
}

Let’s take a look how I handle the above mentioned event FlexEvent.INIT_PROGRESS:

private var readyToAdvanceToSecondFrame : Boolean = false;
private var pendingInitProgressEvent : FlexEvent;
public function onPreloaderInitProgress(e : FlexEvent) : void
{
    if (readyToAdvanceToSecondFrame)
    {
        pendingInitProgressEvent = e.clone();
        e.stopImmediatePropagation();
    }
}

It worths taking note on the way I’m subscribing to FlexEvent.INIT_PROGRESS:

_preloader.addEventListener(FlexEvent.INIT_PROGRESS, onPreloaderInitProgress,
    false, int.MAX_VALUE);

I’m setting the priority of the specified handler to the maximum possible value, making this handler the first one to execute when the event is dispatched.

Duplicating Binary Class Definitions

This probably needs a separate post, but I’ll share it here anyway. It is very possible that you’ll end up using a Flash component as your Preloader. The reasons for such a solution are clear. If your Preloader does a lot of stuff before your main application is even loaded, this better be a separate component. If you choose to put it in the application it will first increase the size of it, and second you’ll need to wait loading the whole application before using it. But making it separate imposes the question whether to use Flash or Flex when implementing it. Making this component a separate Flex application means you’ll have a preloader for the actual preloader, which is acceptable if you find a way how to load and use one Flex application inside of another Flex application. If you’ve done this, please share it with us and write a comment about it. Another thing we can’t rely on is whether the Flex framework is loaded - the size of the framework can be much bigger than the size of the Preloader you want to use.

Well, let’s assume we end up using a Preloader component written in Flash. When I did this I ran into a very subtle problem - duplicating binary class definitions. The picture below illustrates the problem:
Flex Application Domains
The Bombay Application Domain is the domain with the code of the main application. If this code holds a reference to the concrete type of the Flash-component, represented here by the LDA Application Domain we’ll run into trouble if the above application domain contains definitions of same classes - in this example the problematic duplication is the Tweener class. Such a duplication makes the application behave strange and unpredictable.

One way to avoid ugly situations like these is to use the exclude command before compiling the Preloader Flash component. The Flash 8 IDE provided this feature, but unfortunately it was removed from the new Flash IDE that has no such exclude definitions command.

Another way to avoid duplication is to avoid the above application domain to reference the concrete type of the Flash component. All you need to do is to encapsulate the preloader component under an interface Facade, thus the Flash component will conform with the Facade interface and the above application domain will not need to know the concrete type of the Flash component.

A Skeleton Implementation

I want to finish with a skeleton implementation, so you can base your code on it.

IPreloaderContentFacade.as:

package preloader
{
import flash.events.IEventDispatcher;

public interface IPreloaderContentFacade extends IEventDispatcher
{
    function setApplicationProgress(bytesLoaded : Number, bytesTotal : Number) : void;
}
}

TotallyCustomPreloader.as:

package preloader
{
import flash.display.Loader;
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;

import mx.events.FlexEvent;
import mx.preloaders.IPreloaderDisplay;

public class TotallyCustomPreloader extends MovieClip implements IPreloaderDisplay
{
    public static const PRELOADER_CONTENT_URL : String = "preloader_content.swf";

    public var backgroundAlpha : Number;
    public var backgroundColor : uint;
    public var backgroundImage : Object;
    public var backgroundSize : String;
    public var stageHeight : Number;
    public var stageWidth : Number;

    private var _preloader : Sprite;
    public function set preloader(value : Sprite) : void
    {
        _preloader = value;

        _preloader.addEventListener(ProgressEvent.PROGRESS, onPreloaderProgress);
        _preloader.addEventListener(FlexEvent.INIT_PROGRESS, onPreloaderInitProgress,
            false, int.MAX_VALUE);
        _preloader.addEventListener(FlexEvent.INIT_COMPLETE, onPreloaderComplete);
    }

    private var preloaderContentFacade : IPreloaderContentFacade;

    public function TotallyCustomPreloader()
    {
        super();
        loadPreloaderContent();
    }

    public function initialize() : void
    {
    }

    private var loader : Loader;
    private function loadPreloaderContent() : void
    {
        loader = new Loader();
	loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);

	var loaderContext : LoaderContext = new LoaderContext(false, new ApplicationDomain());
	var request : URLRequest = new URLRequest(PRELOADER_CONTENT_URL);
	loader.load(request, loaderContext);
    }

    private function onLoaderComplete(e : Event) : void
    {
        preloaderContentFacade = IPreloaderContentFacade(loader.content);
        addChild(MovieClip(preloaderContentFacade));
    }

    private function onPreloaderProgress(e : ProgressEvent) : void
    {
        if (preloaderContentFacade != null)
        {
            preloaderContentFacade.setApplicationProgress(e.bytesLoaded, e.bytesTotal);
        }
    }

    private var readyToAdvanceToSecondFrame : Boolean = false;
    private var pendingInitProgressEvent : FlexEvent;
    private function onPreloaderInitProgress(e : FlexEvent) : void
    {
        if (readyToAdvanceToSecondFrame)
        {
            pendingInitProgressEvent = e.clone();
            e.stopImmediatePropagation();
        }
    }

    private function onPreloaderComplete(e : FlexEvent) : void
    {
        readyToAdvanceToSecondFrame = true;
        if (pendingInitProgressEvent != null)
        {
            dispatchEvent(pendingInitProgressEvent);
        }
        dispatchEvent(new Event(Event.COMPLETE));
    }
}
}

This skeleton implementation is omitting a lot of things a real implementation can have - all depends on the specific requirements of the application you develop. But I think the code above is general enough to serve as a guideline for a concrete implementation.


This post is tagged , , , ,

4 Comments

  1. Nice explanation of how preloader works. I am using something similar to your custom preloader. However, i really like your skeleton implementation code. Are you planning to license that?

    I have a question, though. How can i create a popup that appears the moment SystemManager switches to the Flex application (Frame 2)? When i put my popup creation code in the creationComplete event, it pops up during the preloader.

  2. admin

    Hi, Kenneth,

    The information in my blog is covered by Creative Commons (check the footer for more details), but Creative Commons is not meant for licensing code, so feel free to use the skeleton code I’ve provided in this article under the MIT license. Grouping the code with this comment has actually the power of signing the code with the license of my choice:

    Copyright (c) 2008 Vladimir Tsvetkov
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.
    

    Now, to the questions:
    If I’m getting you right it seems to me you’re trying to do the following:

    dispatchEvent(new Event(Event.COMPLETE));
    PopUpManager.createPopUp( ... );
    

    If you’re doing this the popup will appear before the Preloader has been removed from the display. The Event.COMPLETE will cause the SystemManager to execute preloader_preloaderDoneHandler(), which will remove the Preloader and then will add the Application to the display. But creating your popup is such a manner is too early. You can try opening this popup on Application addedToStage - something like that:

    <mx:Application addedToStage="PopUpManager.createPopUp( ... );" ...
    

    Cheers

  3. Wow.. your suggestion “addedToStage” helped! i was looking for something instead of “show” or “creationComplete”.

    Regarding your code license, i noticed your Creative Commons logo on your blog. I’ll keep in mind your license.

    Thanks a lot!

Leave a Reply