Communicating with Modules

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.

ReferenceCommunicationDemo

Figure 7-4. ReferenceCommunicationDemo

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>
EventCommunicationDemo application

Figure 7-5. EventCommunicationDemo application

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.120.206