Modular Applications (part 2)

So, back to where we were going before the digression on ApplicationDomain. One effective way of partitioning an application is to create an application shell that dynamically loads modules.

What exactly should a module look like?


There are a couple choices here. One possibility would be to just build a SWC, and extract out the SWF, and use it as a “bag of classes” via getDefinition. Not particularly elegant.Another possibility would be to build the module as an application. This is probably somewhat wasteful, because most applications carry some runtime infrastructure with them.Or, perhaps just build something extending a Flash class like Sprite or MovieClip. Not a bad possibility, but you’ll probably need to just use it as a bag of classes again, unless you intend to use your modules as pure visual elements.My preference is to build a new base class (lets call it “ModuleBase”!) that takes advantage of the semi-secret and bizarre [Frame] metadata that the Flex compiler uses to build multiframe Flash movies.What? You’ve never heard of [Frame]? Take a peek in mx.core.Application’s source, and you’ll see:

[Frame(factoryClass="mx.managers.SystemManager")]

or take a look in mx.core.SimpleApplication, and you’ll see:

[Frame(factoryClass="mx.core.FlexApplicationBootstrap")]

This metadata is a hint to the compiler that the class containing the metadata would like to be bootstrapped by the cited factory.In other words, when you build an application based on mx.core.Application, that isn’t actually the “root” class of the SWF. The root class is actually a compiler-generated subclass of mx.managers.SystemManager that overrides the info() method. Turn on “keep-generated-actionscript” during your compile, and you can see what the compiler generates.The rules are a bit weird, but basically: if the [Frame] metadata exists on the base class of your application, a subclass of the factory will be generated. If the metadata is directly on your application class, it will be honored, but no subclass will be generated; its assumed that you’ve already written the appropriate factory.When a frame factory class has been specified, the compiler will basically create a multi-frame movie, where the factory class is on the previous frame. It can arbitrarily daisy-chain these frames; you could (although why?) build a 10-frame bootstrapped movie this way. Note that the metadata is actually just an inline alias for the “frames” compiler configuration option, which lets you explicitly specify the frame classes.I personally like the notion of module SWFs being multiframe, because (a) it allows the module to use RSLs (which must be loaded before the main class), (b) it allows some interaction with the loaded content as soon as the INIT event fires, and (c) the generated factory subclass is how the compiler generates some information like the list of embedded fonts.Its really just personal preference. The one thing I do believe is useful, though, is to have the root class of the SWF be a factory class. So, if you do extend Sprite or MovieClip, consider implementing some sort of interface that can be used to create the classes within your module.To explore this concept, I’ve written the beginnings of a module system, with a demo that exercises it.Here is a ZIP file containing the demo.The demo uses a small utility library built around a ModuleManager singleton that handles the complexities of loading modules. Any given module URL is either in an unloaded or loaded state. After requesting that a module be loaded, an IModuleInfo interface is available. Once the module is loaded, a factory can be obtained that can be used to create an instance of whatever the module implements. The modules rely on a factory class to issue a “ready” event when it is safe to invoke the create() function.The demo exploits this system by having a shell application that loads an XML file containing elements that correspond to SWF file names; for example, if a “square” element is encountered, it tries to find “square.swf”, load it as a module, and then invoke the IRenderer interface on the module. Each module is simply expected to implement this small interface to draw itself.The code for this demo is pretty simple, and will evolve over time, hopefully into an actual supported library. For now, its just a small skunkworks project, so enjoy your advanced peek!