InitMonitor: Yet Another PureMVC Initialization Manager [AS3] · Oct 9, 06:23 PM

Updated: 10.02.2008
InitMonitor is an initialization manager designed for simplicity and flexibility. Admittedly, overall not as full featured as some other solutions out there. However, unlike most others, this manager is intended to be used (and reused) at any stage in your application, not just at startup. InitMonitor was designed in conjunction with my PureMVC SWFAddress implementation and works well in a situation where application start-up can mean many different things depending on the entry point URL.

InitMonitor Documentation

Download InitMonitor

The InitCommand

At it’s heart, InitMonitor, via InitMonitorProxy sequentially invokes a load() function on a bunch of Proxies. It makes sense to define the order of invocation in a PureMVC Command. For example, in my application I have a command called EatInitCommand. (This is one of the first commands that gets executed when you go here). The EatInitCommand defines an array named proxies:

  1. private var proxies:Array = [
  2. [ AppInitProxy.NAME,
  3. TextScrollerInitProxy.NAME,
  4. _GaramondInitProxy.NAME,
  5. _TrebuchetMSInitProxy.NAME,
  6. _ZeroesInitProxy.NAME ],
  7. Eat3DInitProxy.NAME,
  8. EatTitlesRemoteInitProxy.NAME,
  9. StyleInitProxy.NAME,
  10. EatRemoteInitProxy.NAME
  11. ];

Notice that the array elements are the names of proxies, and not proxies themselves. Also notice that the first element of the proxies array is an array. The nested array tells InitMonitor that all items in this array should be loaded as a group, asynchronously. Therefore, the InitMonitorProxy will immediately invoke the load() function on: AppInitProxy, TextScrollerInitProxy, __GaramondInitProxy, __TrebuchetMSInitProxy, __ZeroesInitProxy. When all of these proxies have completed loading, InitMonitorProxy will invoke load() on Eat3DInitProxy. When Eat3DInitProxy is complete, InitMonitorProxy will invoke load() on EatTitlesRemoteInitProxy and so forth until the last proxy is completed.

InitMonitor does not support explicit dependencies the way StartupManager does, and opts to take a much simpler approach where dependencies are implied by the order they are added to the array.

Now take a look at the full code for the EatInitCommand class:

  1. public class EatInitCommand extends SimpleCommand implements ICommand {
  2. private var monitor:InitMonitorProxy;
  3. private var proxies:Array = [
  4. [ AppInitProxy.NAME, TextScrollerInitProxy.NAME, _GaramondInitProxy.NAME, _TrebuchetMSInitProxy.NAME, _ZeroesInitProxy.NAME ],
  5. Eat3DInitProxy.NAME, EatTitlesRemoteInitProxy.NAME, StyleInitProxy.NAME, EatRemoteInitProxy.NAME
  6. ];
  7.  
  8. override public function execute(notification:INotification):void {
  9. super.execute(notification);
  10.  
  11. // Retrieve the InitMonitorProxy object
  12. monitor = (facade.retrieveProxy( InitMonitorProxy.NAME ) as InitMonitorProxy);
  13.  
  14. // the notification body contains an array representation of the current URL
  15. var uriSegments:Array = notification.getBody() as Array;
  16.  
  17. // Feed the current URI to EatRemoteInitProxy so that it will retrieve the correct XML via Remoting
  18. var _EatRemoteInitProxy:EatRemoteInitProxy = (facade.retrieveProxy( EatRemoteInitProxy.NAME ) as EatRemoteInitProxy);
  19. _EatRemoteInitProxy.uriSegments = uriSegments;
  20.  
  21. // always call stop() first
  22. monitor.stop();
  23.  
  24. // We listen for this notification in EatMediator
  25. monitor.completeNotification = new Notification(ApplicationFacade.EAT_INIT_COMPLETE);
  26.  
  27. monitor.addResources(proxies);
  28.  
  29. // Nothing actually happens until we call InitMonitorProxy.start()
  30. monitor.start();
  31. }
  32. }

The code above should seem pretty straightforward. First we retrieve InitMonitorProxy which is the Proxy that manages the order in which each Proxy in the proxies array will load. Then, in lines 18 & 19 we perform some initialization on one of the proxies so it knows what content to load. Finally, we initialize InitMonitorProxy by calling stop(), then addResources(), and finally start(). After all the proxies have been completed, InitMonitorProxy will send the notification instantiated on line 25.

The InitProxy

We call each proxy that represents a step in the initialization process an InitProxy. For example, my proxy _GaramondInitProxy is the InitProxy responsible for loading a file named _Garamond.swf which contains the Garamond font. An InitProxy must extend InitSimpleProxy implement the IInitProxy interface.

What exactly an InitProxy does is up to you. However, it is a good idea to construct each InitProxy so that it performs a discreet initialization step. For example, while you could create an InitProxy that loads in all your apps fonts, it may be advantageous to load each font from it’s own InitProxy. This way, different sections of your app may load only the fonts it needs, thus saving memory.

Because I found myself often creating InitProxies performing similar tasks, I created subclass of InitSimpleProxy called FontAssetProxy and BulkAssetsProxy. (Instead of sublassing InitSimpleProxy, some of my InitProxies subclass FontAssetProxy or BulkAssetsProxy.) FontAssetProxy is responsible for loading a single .swf file containing a font. BulkAssetsProxy uses the OpenSource library BulkLoader, and can be used to create an InitProxy that loads any number of files.

Take a look at the source for FontAssetProxy:

  1. package com.afw.puremvc.InitMonitor.model {
  2. import flash.events.Event;
  3. import br.com.stimuli.loading.BulkLoader;
  4. import com.afw.puremvc.InitMonitor.model.BulkAssetsProxy;
  5. import com.afw.puremvc.InitMonitor.model.IInitProxy;
  6.  
  7. public class FontAssetProxy extends BulkAssetsProxy implements IInitProxy {
  8. private var fontFile:String;
  9.  
  10. public function FontAssetProxy(proxyName:String, fontFile:String ) {
  11. this.fontFile = fontFile;
  12. super(proxyName, Number(0) );
  13. }
  14.  
  15. public override function load():void {
  16. if (busy) return;
  17. _busy = true;
  18.  
  19. loader.add(fontFile, { context: myContext } );
  20. loader.addEventListener(BulkLoader.COMPLETE, onAssetsLoaded);
  21. loader.start();
  22. }
  23.  
  24. private function onAssetsLoaded(evt:Event = null):void {
  25. _complete = true;
  26. _busy = false;
  27. finish();
  28. }
  29. }
  30. }

In the load() function, return finish() is called if the font has already loaded Why would we do this? Throughout the course of our app, we may try to load a specific font several different times, but after it is loaded the first time there is no point in trying to load it again.
The onAssetsLoaded function gets called when the font file has completed loading. At this point, we must set _complete = true so that future attempts to load the font won’t reload it as just pointed out. Finally, the finish() function gets called. finish(), defined in InitSimpleProxy, sends a notification which allows InitMonitorProxy to know when this proxy has completed processing.
Update 10.2.08: If at some later time, the same FontAssetProxy is again added as a resource to InitMonitorProxy, because the FontAssetProxy is already complete InitMonitorProxy will simply call finish() instead of load(). Keep in mind that finish() should always get called (thus the completeNotification is sent regardless of whether or not complete is true).

Now take a look at how you would subclass FontAssetProxy:

  1. package com.hookah.model {
  2. import com.afw.puremvc.InitMonitor.model.FontAssetProxy;
  3. import com.afw.puremvc.InitMonitor.model.IInitProxy;
  4. import com.hookah.ApplicationFacade;
  5.  
  6. public class _GaramondInitProxy extends FontAssetProxy implements IInitProxy {
  7. public static const NAME:String = '_GaramondInitProxy';
  8. public static const FONT_FILE:String = ApplicationFacade.FONT_Garamond;
  9.  
  10. public override function get completeNotificationName():String {
  11. return NAME+'_COMPLETE';
  12. }
  13.  
  14. public function _GaramondInitProxy() {
  15. super( NAME, FONT_FILE );
  16. }
  17. }
  18. }

The constructor simply passes on the font’s filename to the superclass, FontAssetProxy. The getter completeNotificationName returns the string __GaramondInitProxy_COMPLETE. The completeNotificationName is the notification that InitSimpleProxy.finish() sends, as discussed above. Notice that to create another InitProxy for a different font, all you would have to do to this class is change the name of the class, and the first two static variables, NAME and FONT_FILE.

Structuring your App

The nice thing about the InitMonitor is that it provides a way to structure a complex app using SWFAddress in a very elegant, straight-forward way. By implementing this utility in your PureMVC app, you will be able to condense the major steps of your app into any number of different Commands. You’ve already seen how I constructed my EatInitCommand, now take a look at the proxies arrays of a few more commands from the same app:

HomeInitCommand (related to this section):

  1. private var proxies:Array = [
  2. AppInitProxy.NAME,
  3. Home3DInitProxy.NAME,
  4. HomeImageInitProxy.NAME,
  5. GalleryRemoteInitProxy.NAME,
  6. GalleryInitProxy.NAME,
  7. PanoramaIconInitProxy.NAME
  8. ];

LocationInitCommand (related to this section):

  1. private var proxies:Array = [
  2. [ AppInitProxy.NAME,
  3. TextScrollerInitProxy.NAME,
  4. _GaramondInitProxy.NAME,
  5. _TrebuchetMSInitProxy.NAME ],
  6. Location3DInitProxy.NAME,
  7. StyleInitProxy.NAME,
  8. LocationRemoteInitProxy.NAME
  9. ];

ReservationsInitCommand (related to this section):

  1. private var proxies:Array = [
  2. [ AppInitProxy.NAME,
  3. TextScrollerInitProxy.NAME,
  4. _GaramondInitProxy.NAME,
  5. _TrebuchetMSInitProxy.NAME,
  6. _ZeroesInitProxy.NAME ],
  7. Reservations3DInitProxy.NAME,
  8. ReservationsTitlesRemoteInitProxy.NAME,
  9. StyleInitProxy.NAME,
  10. ReservationsRemoteInitProxy.NAME
  11. ];

What else?

I don’t have any fully-functional sample code to offer, yet. This article did not fully examine how to use the utility, however in time, more information will be made available. For now, the best way to learn to use the utility is to read the source code. The implementation should be pretty straightforward and easy to understand.

— Pickle

---

Comment

  1. The finish method should return a Boolean, ain’t it ?

    erixtekila · Sep 23, 01:17 AM · #

  2. finish() does not return anything (ie it returns void). This line:

    if (complete) return finish();

    is short for:

    if (complete) {
    finish();
    return;
    }

    This is possible because both load() and finish() are defined as type void. I’ve turned a 4-line if statement into a 1-liner.

    BTW, the only thing finish() does is send a notification so that the InitMonitorProxy knows that this particular proxy has completed. The finish function is inherited from the InitSimpleProxy class.

    Pickle · Sep 23, 08:15 AM · #

  3. <quote>is short for</quote>
    Thanks for the teaching ;)
    I was meaning that using a function that doesn’t return anything is a fairly strange way.
    Wht are the benefit of this oneliner statement ?
    My Flex doesn’t like that… at all !

    erixtekila · Sep 29, 11:55 PM · #

  4. erixtekila,

    Thank you for bringing this up. I have not yet tested this in Flex so I did not know there was a problem.
    There is no inherent advantage to the one-liner, in fact it still confuses me sometimes.

    Another possibility here is to have the InitMonitorProxy check for completeness so you would only ever touch complete if you don’t want the same xxxInitProxy to load more than once. This might make it simpler and easier to use. I will do some testing and try to post an update in the next couple of days if I find this to be the case.

    Pickle · Sep 30, 07:57 AM · #

  5. I have updated the code and article to include the changes discussed above. The docs still need to be updated, though. Also, I have not tested in Flex, but it should work.

    Pickle · Oct 1, 10:55 PM · #

Textile Help