You’ve designed your modules to be independent, but there should be provisions to allow external applications to communicate with them, pass them some information and receive response notifications. From the user’s point of view, it may look like an innocent drag-and-drop action, but internally you must resort to one of the several available means of communication. We will start with direct references to the module variables and methods.
First, consider the method-based interfaces. We’ll assume that you
have the IGreeting
interface, as shown in Example 7-12.
Example 7-12. IGreeting interface
//IGreeting.as package { public interface IGreeting { function getGreeting():String; function setGreeting( value:String ):void; } }
Further, suppose that a module, such as ModuleWithIGreeting
in Example 7-13, is
implementing this interface. Please notice that
calling setGreeting()
will modify the
bindable variable greeting
that affects
the title of the module’s panel.
Example 7-13. Example of a module implementing the IGreeting interface
<?xml version="1.0"?>
<!- ModuleWithIGreeting.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
implements="IGreeting"
creationComplete="onCreationComplete()"
>
<mx:Script>
<![CDATA[
[Bindable] private var greeting:String="";
public function setGreeting(value:String):void {
greeting = value;
}
public function getGreeting():String {
return greeting;
}
]>
</mx:Script>
<mx:Panel id="panel" title="Module With Greeting{greeting}" width="400"
height="200">
</mx:Panel>
</mx:Module>
How can your application take advantage of the fact that the loaded
module implements a known interface? Assuming that it has used a ModuleLoader
, as the following snippet shows,
you can cast its child
property to the
IGreeting
interface:
var greeting:IGreeting = moduleLoader.child as IGreeting; greeting.setGreeting(" loaded by application");
Then again, no one prevents you from simply referencing the panel from ModuleWithIGreeting by name:
var module:Module = moduleLoader.child as Module; var panel:Panel = module.getChildByName("panel") as Panel; trace(panel.title); //Simple Module loaded by application
The complete ReferenceCommunicationDemo application is presented in Example 7-14.
Example 7-14. ReferenceCommunicationDemo application
<?xml version="1.0"?> <!-- ReferenceCommunicationDemo.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import mx.modules.Module; import mx.containers.Panel; private const MODULE_URL:String="ModuleWithIGreeting.swf"; private function modifyLoadedContent():void { var greeting:IGreeting = moduleLoader.child as IGreeting; greeting.setGreeting(" loaded by application"); var module:Module = moduleLoader.child as Module; var panel:Panel = module.getChildByName("panel") as Panel; trace(panel.title); //Simple Module loaded by application } ]]> </mx:Script> <mx:HBox> <mx:Button label="Load Module" click="moduleLoader.loadModule(MODULE_URL)" /> <mx:Button label="Modify Content" click="modifyLoadedContent()"/> <mx:Button label="Unload Module" click="moduleLoader.unloadModule()" enabled="{moduleLoader.loaderInfo.bytesTotal!=0}"/> </mx:HBox> <mx:ModuleLoader id="moduleLoader"/> </mx:Application>
This application has three buttons labeled Load Module, Modify Content, and Unload Module (Figure 7-4), each associated with a similarly named function. This separation of functions enables you to profile the application and verify that there is no memory leak associated with module unloading.
Although this interface-based method of working with modules is appealing, use it with care: it uses direct references to the modules, and any unreleased direct reference will indefinitely lock your module in memory. Against this backdrop, the elegance of the interfaces does not matter much.
The best way to make sure you do not have unreleased references is
to avoid them to begin with. Instead, use events to
communicate with the loaded modules. To do so, you need an EventDispatcher
that can be commonly accessed by
the module and the loading application (here’s yet another example of the
Mediator design pattern from Chapter 2).
One object that suits the task particularly well is sharedEvents
, accessible as loader.loaderInfo.sharedEvents
from the module
and loading application as well.
The complete code of the sample application EventCommunicationDemo
is presented in Example 7-15. Note that in the loadModule()
, you subscribe to Event.COMPLETE
to be sent by the modules upon
loading and creating the module’s display list. Then the onComplete()
handler application itself sends an
event to the module. The module, as you will see soon, interprets this
event to modify a panel’s header.
Example 7-15. EventCommunicationDemo application
<?xml version="1.0"?> <!-- EventCommunicationDemo.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import mx.events.DynamicEvent; import mx.controls.Alert; import mx.events.ModuleEvent; import mx.modules.Module; private const MODULE_URL:String="ModuleWithEvents.swf"; [Bindable] private var moduleLoaded:Boolean; private function loadModule():void { // Subscribe to notifications from the module var sharedEventDispatcher:IEventDispatcher = moduleLoader.loaderInfo.sharedEvents; sharedEventDispatcher.addEventListener( Event.COMPLETE, onModuleCreated ); moduleLoader.loadModule(MODULE_URL); moduleLoaded = true; } // This event "comes" from the module private function onModuleCreated(event:Event):void { trace("Module CreateComplete happened"); //Send commands to the module var sharedEventDispatcher:IEventDispatcher = moduleLoader.loaderInfo.sharedEvents; var dynamicEvent:DynamicEvent = new DynamicEvent("command"); dynamicEvent.data = " Two-way talk works!"; sharedEventDispatcher.dispatchEvent(dynamicEvent); } private function unloadModule():void { moduleLoader.unloadModule(); moduleLoaded = false; } ]]> </mx:Script> <mx:HBox> <mx:Button label="Load Module" click="loadModule()" /> <mx:Button label="Unload Module" click="unloadModule()" enabled="{moduleLoaded}"/> </mx:HBox> <mx:ModuleLoader id="moduleLoader"/> </mx:Application>
Example 7-16 presents
the corresponding module sample ModuleWithEvents
. Notice the handler of the
creationComplete
event. It subscribes
to the command events sent by the application and
notifies the application that the module is ready for receiving such
events by dispatching Event.COMPLETE
.
The syntax of addEventListener()
specifies weak reference, because strong reference to
the sharedEventDispatcher
would prevent
the module from being garbage-collected. If you run the application and
click on the button Load Module, you will see the screen shown in Figure 7-5.
The panel’s header will read “Module With Events. Two-way talk works!” to emphasize the fact that the application and the module exchange events in both directions. You may want to actually profile the application and watch how referencing of the event listener (weak versus strong) dramatically affects the ability to unload the module.
Example 7-16. Counterpart module example to EventCommunicationDemo
<?xml version="1.0"?> <!- ModuleWithEvents.mxml --> <mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="onCreationComplete()" > <mx:Script> <![CDATA[ import mx.events.DynamicEvent; [Bindable] private var command:String=""; private function onCreationComplete():void { var sharedEventDispatcher:IEventDispatcher = systemManager.loaderInfo.sharedEvents //Subscribe to command from the application sharedEventDispatcher.addEventListener( "command", onCommand,false,0,true ); //Strong reference would lock the module to application // Notify the applications that creation has completed sharedEventDispatcher.dispatchEvent(new Event(Event.COMPLETE) ); } private function onCommand(event:DynamicEvent):void { command = event.data as String; } ]]> </mx:Script> <mx:Panel id="panel" title="Module With Events. {command}" width="400" height="200"/> </mx:Module>
18.118.120.206