6 Replies Latest reply on Mar 3, 2009 5:18 PM by Moenizzle

    Flex Preloader and Application Life Cycle

    Moenizzle
      I'm trying to create a flex application that preloads an XML file and populates some custom controls before displaying the page to the user. I'm trying to avoid having the controls resize as they populate while the user is viewing.

      I have tried to create a custom preloader that, before dispatching Event.COMPLETE, loads the XML file and makes it available to be populated by the application. The problem is that I can't figure out how to get the application to "block" so that I can populate the controls at a point in the life cycle before they are displayed. My best guess was that I would start loading the XML in the preloaders onInitComplete method and hold off on firing the Event.COMPLETE event until after the XML was loaded. However, the attached code illustrates that the applications creationComplete event is fired before the preloader's onInitComplete event meaning that the only application event available for populating the controls with the XML data is applicationComplete and the controls are already visible which results in components being resized and populated in view of the user.

      I'm wondering if there is another way to ensure that the XML data is loaded before the application renders the controls for the first time.
        • 1. Re: Flex Preloader and Application Life Cycle
          *gsb* Level 1
          I usually have the data load early: before the "CreationComplete" and certainly before the "ApplicationComplete" Flex events are triggered. This is set up and debugged (tested) before I add a preloader.

          My preloaders are an add-on that listen to the Flex events and also some custom preloader specific events, as needed. There may be several like: Settings loaded and processed; XML data loaded and processed; the first 'n' images loaded; ...etc.

          The secret is to catch, stop, and save the Flex "preloader done" event and then finish listening for your custom events if they are still pending. When all have been triggered, send the captured Flex event and kill the preloader.

          This gives you "total" control of what to load and when it is complete.


          • 2. Re: Flex Preloader and Application Life Cycle
            Moenizzle Level 1
            Greg,

            Thanks for the reply!

            I think I may be misunderstanding the interaction between the preloader and the application. If I, for instance, start loading my XML file in the preinitialize or initialize handler of the application there's no guarantee that it will be finished loading before the applicationComplete event is fired, is there? Also, I don't see any way that I can register an event with the preloader since I don't actually have access to the instance of the preloader object in the application (or do I?) since it's designated in the 'preloader="..."' portion of the application tag.

            Anyway, really appreciate your help so far. I will begin experimenting with your idea.

            Cheers!
            Moshe
            • 3. Re: Flex Preloader and Application Life Cycle
              Moenizzle Level 1
              quote:

              The secret is to catch, stop, and save the Flex "preloader done" event and then finish listening for your custom events if they are still pending


              Can you tell me what the precise name of the "preloader done" event is that you're referring to? Is it the Event.COMPLETE event or something else?

              Thanks!
              Moshe
              • 4. Re: Flex Preloader and Application Life Cycle
                *gsb* Level 1
                Hi. I am not the expert on the subject but have read enough and tested enough to have a workable solution. I start by making a class as:

                public class CustomSM2SWF8Preloader extends Sprite implements IPreloaderDisplay
                { … }

                Read here for the IpreloaderDisplay contract:
                http://livedocs.adobe.com/flex/3/langref/mx/preloaders/IPreloaderDisplay.html

                Among others you will make a setter/getter for the interface's preloader property, a Sprite, like:

                private var _preloader:Sprite;
                public function get preloader() : Sprite
                { return _preloader; }

                public function set preloader( value:Sprite ) : void
                {
                . . //trace("Preloader instantiated...");
                . . _preloader = value;
                . . _preloader.addEventListener( ProgressEvent.PROGRESS, onPreloaderProgress );
                . . _preloader.addEventListener( Event.COMPLETE, onPreloaderComplete, false, int.MAX_VALUE );
                . . _preloader.addEventListener( FlexEvent.INIT_PROGRESS, onPreloaderInitProgress, false, int.MAX_VALUE );
                . . _preloader.addEventListener( FlexEvent.INIT_COMPLETE, onPreloaderInitComplete, false, int.MAX_VALUE );

                . . stage.addEventListener( ResizeEvent.RESIZE, onResizeHandler );
                . . onResizeHandler( {} );
                }

                Note that on the event handlers you set the priority. Read up but basically this helps set the event processing order.

                I use a number of variables to track and record / coordinate the asynchronous loading process:

                private var readyToInitializeApplication:Boolean = false;
                private var readyToAdvanceToSecondFrame:Boolean = false;
                private var dataInitializationComplete:Boolean = false;
                private var appliationListenerSet:Boolean = false;
                private var pendingInitProgressEvent:FlexEvent;

                Here is where the normal preloader is satisfied:

                private function onPreloaderInitProgress( event:FlexEvent ) : void
                {. . // Timing, timing, timing, ... its all about timing.
                . . if( ! readyToInitializeApplication ) readyToInitializeApplication = true;
                . . else if( readyToInitializeApplication && ! appliationListenerSet )
                . . {
                . . . . Application.application.addEventListener( CustomEvent.XML_DATA_ARRIVAL, onXMLDataArrival, false, int.MAX_VALUE );
                . . . . // Application.application.addEventListener( CustomEvent.IMAGE_PRELOAD_COMPLETE, onImagesPreloaded, false, int.MAX_VALUE );
                . . . . appliationListenerSet = true;
                . . }
                . . _clip.preloader.caption._text.text = "...initializing...";
                . . if( readyToAdvanceToSecondFrame )
                . . {
                . . . . // For now, copy and stop this event from being processed by the application.
                . . . . //trace("readyToAdvanceToSecondFrame... save pending event.");
                . . . . pendingInitProgressEvent = event.clone() as FlexEvent;
                . . . . event.stopImmediatePropagation();
                . . }
                }

                At this point, the preloader is still up, the application is initialized and we are waiting for our custom events like: XML_DATA_ARRIVAL, IMAGE_PRELOAD_COMPLETE and/or what ever you need/set. When these are also satisfied, we send our cloned event:

                private function onXMLDataArrival( event:CustomEvent ) : void
                {
                . . dataInitializationComplete = true;
                . . if( readyToAdvanceToSecondFrame ) onPreloaderFadeOut();
                }

                In my case, I have added a second or two to fade out the preloader, onPreloaderFadeOut(). But upon completion of this transition, we clean up and start the real application; loaded and ready to go:

                private function onDoneTransitioning() : void
                {
                . . _clip.stop();
                . . _preloader.removeEventListener( ProgressEvent.PROGRESS, onPreloaderProgress );
                . . _preloader.removeEventListener( Event.COMPLETE, onPreloaderComplete );
                . . _preloader.removeEventListener( FlexEvent.INIT_PROGRESS, onPreloaderInitProgress );
                . . _preloader.removeEventListener( FlexEvent.INIT_COMPLETE, onPreloaderInitComplete );

                . . stage.removeEventListener( ResizeEvent.RESIZE, onResizeHandler );

                . . Application.application.removeEventListener( CustomEvent.XML_DATA_ARRIVAL, onXMLDataArrival );
                . . // Application.application.removeEventListener( CustomEvent.IMAGE_PRELOAD_COMPLETE, onImagesPreloaded );

                . . if( pendingInitProgressEvent != null )
                . . . . dispatchEvent( pendingInitProgressEvent );

                . . // ...transfer control to the Flex application now.
                . . dispatchEvent( new Event( Event.COMPLETE ) );
                }


                So that or a version there of, is what I use...
                • 5. Re: Flex Preloader and Application Life Cycle
                  Moenizzle Level 1
                  Well, I suspect that it wasn't technically the best way of doing it but I found a solution...

                  The key was apparently to populate the controls before the preloader fired the Event.COMPLETE event (which Greg illuded to), which is some time before the applicationComplete event of the application is fired. The problem was that I couldn't figure out how to get communication going between the preloader and the application. In the initialize() method of the application, for instance, this.preloader was null. Likewise, in the constructor of the preloader mx.core.Application.application was null and I couldn't figure out any other way to get a reference to the application.

                  To get around this--and warning: this is ugly--I created two public static variables (basically globals) in my CustomPreloader class:
                  public static var xmlFileReady:Boolean;
                  public static var applicationReady:Boolean;

                  And then I just used timers in both the application and preloader classes to run through this routine:
                  1) Application starts a timer in its initialize handler which fires as often as possible and continually checks the xmlFileReady to see if its true
                  2) The preloader does it's standard preloading but instead of stopping there continues on to preload the XML. Once the XML is loaded it sets the xmlFileReady to true THEN launches a timer that fires as often as possible and waits for applicationReady to be true
                  3) The application, finding xmlFileReady to be true, accesses the XML data from yet another static variable and populates the controls. It then sets the applicationReady variable to true
                  4) The preloader, finding applicationReady to be true, dispatches the Event.COMPLETE event to end preloading
                  5) The applicationComplete event DOES NOT end up firing until all of this is done, thankfully, and the controls are all sized and ready to go when the preloader disappears.

                  I've distilled my code into the illustration below. Hopefully this is helpful to anyone who has a similar problem, though there has to be a better way to do this

                  Cheers!


                  • 6. Re: Flex Preloader and Application Life Cycle
                    Moenizzle Level 1
                    Greg,

                    Thanks for the example. Your solution is much more elegant! I am in the process of implementing it instead.

                    Thanks again!
                    Moshe