Garbage Collection in the UcompOS RPF

OscarTheGrouch In large and sophisticated Flash-based applications, such as the Rich Portal Applications you develop with the UcompOS Rich Productivity Framework, Garbage Collection becomes an increasingly important consideration.

Garbage Collection is a computer science concept that deals with memory management and re-allocating memory occupied by objects that are no longer needed or in use by the computer program.

Garbage Collection in a large-scale event-driven Flash architecture is a crucial consideration and unfortunately, can also be a tedious proposition to properly implement.

One of the more important considerations in aiding with Garbage Collection in Flash-based application is proper management of event listeners.

In a best-practice implementation, any call to addEventListener() would be followed up with a call to removeEventListener() once the event listener was no longer needed. Event listeners perpetuate the objects they are associated with so even once a particular object is destroyed, that doesn’t necessarily mean any event listeners associated with it will be destroyed too.

Failure to remove event listeners once they are no longer needed in a large-scale implementation can cause memory leaks and performance can break down over time.

In all your Flash applications, you should employ this strategy when and where possible.

In a typical large-scale UcompOS RPF implementation, Flash-based applications and sub-applications are continuously being loaded and unloaded into the UcompOS Portal.

The concept of Proxy Components in the UcompOS RPF involves event listeners being attached to a Proxy Component in one entity that represents an object in another UcompOS entity and then listening for dispatched events.

Therefore, the concept of garbage collection takes on especially critical importance in the UcompOS RPF because if UcompOS applications are being loaded and unloaded constantly and the event listeners that are attached to them are not being properly removed, performance would become an issue in a large-scale implementation.

The UcompOS Portal has been designed such that when SWF content that has been loaded into the UcompOS Portal is unloaded, a garbage collection routine is implemented to try to optimize system performance. (The three ways SWF content is loaded into the UcompOS Portal is in UcompOS Window instances, UcompOS Artifact instances, or into the UcompOS run-time.)

As of the UcompOS RPF Public Alpha release 0.4.6, some additional enhancements to the garbage collection process have been implemented that I will discuss here.

Understanding the UcompOS RPF garbage collection architecture can help you build more scalable Rich Portal Applications.

The recent enhancements come in the realm of Proxy Components.

By strict convention, all Proxy Components must extend the SDK class AbstractProxyComponent. As of UcompOS RPF 0.4.6, AbstractProxyComponent now extends a new SDK class called SDKEventDispatcher which extends EventDispatcher.

(Also, as a side-note, the AbstractProxyComponent class now implements IEventDispatcher in addition to IProxyComponent so you can refer to your Proxy Components as IEventDispatcher or IProxyComponent instances interchangably.)

SDKEventDispatcher simply overrides addEventListener() and offers a protected property _listeners of type Array.

I chose to create this separate class versus simply overriding addEventListener() in AbstractProxyComponent so that in the event the new clean-up process employed can be useful outside the scope of a Proxy Component, one could simply extend SDKEventDispatcher versus EventDispatcher.

Here is the over-ridden addEventListener():

override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
	_listeners.push({type:type,listener:listener});
	super.addEventListener(type,listener,useCapture,priority,true);
}

Notice that I am always assigning a true value to the useWeakReference parameter in the super call to addEventListener(). This is because in the context of attaching event listeners to Proxy Components, you always would want them to be weak references to aid in garbage collection.

In AbstractProxyComponent, there is a new protected method, cleanup() which is as follows:

protected function cleanup(event:SDKEvent=null):void
{
	while(_listeners.length)
	{
		var object:Object = _listeners.pop();
		removeEventListener(object.type,object.listener);
	}
	_sdkModel.removeProxyComponent(this.uuid);
	var commandObject:CommandObject = new CommandObject("SDK.cleanup",{uuid:this.uuid});
	var dataPackage:DataPackage = new DataPackage(Utilities.createUID(),_sdkModel.connectionId,this.destination,commandObject);
	_sdkClient.send(dataPackage);
}

What the cleanup() method does is it automatically removes any event listeners that have been attached to the Proxy Component.

It also sends a message to the end-point of the Proxy Component targeting its SDK.cleanup public API method that is built into the UcompOS SDK and therefore exposed by any UcompOS entity. This aids in UcompOS RPF garbage collection-related activities.

Calls to cleanup() do not occur automatically.

When you would call cleanup() in a Proxy Component would be an implementation specific consideration.

For instance, with these new capabilities, I have updated some of the UcompOS Portal Proxy Components that are built into the UcompOS SDK.

Consider the updated constructor of the UcompOSWindowProxy class:

public function UcompOSWindowProxy()
{
	super();
 
	// set the destination property to the UcompOS Portal
	super._destination = _sdkModel.root;
	<strong>addEventListener(CLOSE,cleanup);</strong>
}

The call to addEventListener(CLOSE,cleanup) is the new addition to the constructor.

What it does is it causes the inherited cleanup() method to always be called when the UcompOS Window instance on the UcompOS Portal that is represented by the UcompOSWindowProxy instance is closed.

Therefore, any event listeners you have attached to the UcompOSWindowProxy instance are automatically removed whenever the window is closed.

In 100% of the possible use cases for the UcompOSWindowProxy, this is the desired behavior.

The net result of this is you don’t have to worry about making your own calls to removeEventListener() for your particular UcompOSWindowProxy instances.

The same sort of behavior has been engineered into some of the other Proxy Components built into the UcompOS SDK including UcompOSAlertProxy, UcompOSDialogueProxy, UcompOSArtifactProxy, and UcompOSBrowserWindowProxy.

Consider this strategy when you are building your own Proxy Components and remember to always use removeEventListener() in all your other Flash applications whether you are deploying them in the UcompOS Portal or not.

Introducing 3 New UcompOS Portal Proxy Components

The UcompOS SDK in the latest milestone release of the UcompOS Rich Productivity Framework, Public Alpha 0.3.1, contains 3 new UcompOS Portal Proxy Components that open up a host of new capabilities.

In this article, I’ll briefly introduce each of these 3 new components and show some examples of how they can be used and I plan to build some new tutorial content in the near future to cover these 3 new components.

UcompOSKeyboardProxy

Within the scope of an individual UcompOS application or sub-application, you can certainly implement your own logic to handle keyboard events.

However, suppose you want to capture keyboard events that occur in the scope of the UcompOS Portal and pass them to other UcompOS entities such as those that may be running as AIR applications on the desktop, in separate UcompOSBrowserWindowProxy instances, or in sandboxed SWF implementations.

You can now use the new UcompOSKeyboardProxy class to do this.

The class is a Singleton with a single property named listen.

When you set the listen property to true, any keyboard events that occur in the scope of the UcompOS Portal will be dispatched by the UcompOSKeyboardProxy Singleton.

Let’s look at a simple code-based example of this:

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSKeyboardProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class KeyboardExample extends Sprite
	{
 
		public function KeyboardExample()
		{
			SDKClient.getInstance(this);
			UcompOSKeyboardProxy.getInstance().listen = true;
			UcompOSKeyboardProxy.getInstance().addEventListener(UcompOSKeyboardProxy.KEY_DOWN,eventHandler);
			UcompOSKeyboardProxy.getInstance().addEventListener(UcompOSKeyboardProxy.KEY_UP,eventHandler);
		}
 
		private function eventHandler(event:SDKEvent):void
		{
			trace("Type of event: "+event.type);
			trace("Character code: "+event.data.charCode);
			trace("Alt key pressed: "+event.data.altKey);
			trace("Shift key pressed: "+event.data.shiftKey);
			trace("Control key pressed: "+event.data.ctrlKey);
			trace("Key code: "+event.data.keyCode);
		}
 
	}
 
 
}

As you can see, the properties in the event dispatched by the UcompOSKeyboardProxy Singleton are very similar to those of the Flash KeyboardEvent class.

UcompOSAlertProxy

The Flex Alert class is a modal dialogue.  It prevents the user from doing anything until they click a button control on the Alert dialogue.

The UcompOSAlertProxy class lets you throw a Flex Alert onto the UcompOS Portal.

When the user clicks a button on the Alert class, instances of UcompOSAlertProxy dispatch an SDKEvent instance.

The UcompOSAlertProxy class has two methods: confirm() and simpleAlert().

confirm() throws a confirmation style dialogue onto the UcompOS Portal.

Below is the signature of the confirm() method:

public function confirm(title:String,message:String,okLabel:String=null,cancelLabel:String=null,defaultButton:String=OK):void

As you can see, you can customize the alert title, the message of the Alert body, and the labels used on the “OK” and “Cancel” buttons.  You can also indicate which button should be the default button.

Let’s look at a code-based example:

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSAlertProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class AlertExample extends Sprite
	{
 
		public function AlertExample()
		{
			SDKClient.getInstance(this);
			var a:UcompOSAlertProxy = new UcompOSAlertProxy();
			a.addEventListener(UcompOSAlertProxy.ALERT_SUBMIT,eventHandler);
			a.confirm("Confirm","Are you sure you want to delete this file?","Yes!","Hell No!",UcompOSAlertProxy.CANCEL);
		}
 
		private function eventHandler(event:SDKEvent):void
		{
			switch(event.data.detail)
			{
				case UcompOSAlertProxy.OK:
					trace("The file will be deleted");
					break;
				case UcompOSAlertProxy.CANCEL:
					trace("The file will NOT be deleted");
					break;
			}
		}	
 
	}
 
}

Notice in the example above, the SDKEvent‘s data Object will have a detail property that will be a value of UcompOSAlertProxy.OK or UcompOSAlertProxy.SUBMIT.  This is parallel to the implementation of the Alert in Flex.

The simpleAlert() method simply throws an Alert with a single button of type UcompOSAlertProxy.OK.

UcompOSDialogueProxy

In some instances, you’ll want to set up a modal user interaction dialogue when you want to capture some sort of input from the user.

To address this need, I created the UcompOSDialogueProxy class.

The UcompOSDialogueProxy is used to throw up a few different types of modal user interaction dialogues.

For instance, suppose you have a File Management application and you want to give the user the ability to choose a “Save As” option.

You don’t want the user to be able to do anything else until they either input a file name and press submit, or, cancel the operation.

This would be a perfect place to use the new UcompOSDialogueProxy class.

There are initially 4 different types of dialogues I have set up: TextInput for single line text input; TextArea for multiple line text input; RadioButtons for survey-type interaction; and SWFLoader which enables you to throw your own content into the dialogue and this content can be a UcompOS entity.

Below is the signature of the add() method of UcompOSDialogueProxy:

public function add(controlType:String=TEXT_INPUT,title:String="UcompOS Dialogue",label:String="",width:uint=400,height:uint=0,showCloseButton:Boolean=false,cancelLabel:String=CANCEL,submitLabel:String=SUBMIT,cancelEnabled:Boolean=true,submitEnabled:Boolean=true,initData:Object=null):void

Notice the default height is 0.  What happens is when the width or height is 0, the size of the Dialogue automatically conforms to the content that is placed inside of it.  Otherwise, the width/height is explicitly set.

The initData Object parameter has special meaning.

It is passed to the RadioButtons and SWFLoader types of Dialogues and examples of how initData is constructed in those two scenarios is depicted in the examples below.

Let’s look at a code-based example that shows how to use each of the 4 different types of dialogues:

TextInput Dialogue Example

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class TextInputDialogue extends Sprite
	{
 
		public function TextInputDialogue()
		{
			SDKClient.getInstance(this);
			var d:UcompOSDialogueProxy = new UcompOSDialogueProxy();
			d.add(UcompOSDialogueProxy.TEXT_INPUT,"Sample Dialogue","What is your name?");
			d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler);
		}
 
		private function eventHandler(event:SDKEvent):void
		{
			var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy;
			trace("Your name is "+event.data.data);
			d.close();
		}
 
	}
 
}

Notice in the above example, in the eventHandler() method, the UcompOSDialogueProxy‘s close() method is called.  The dialogue does not disappear by itself upon a submission.  You have to explicitly remove it with the close() method such that you have much better control over when the dialogue is closed based on the specifics of the user’s input.

TextArea Dialogue Example

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class TextAreaDialogue extends Sprite
	{
 
		public function TextAreaDialogue()
		{
			SDKClient.getInstance(this);
			var d:UcompOSDialogueProxy = new UcompOSDialogueProxy();
			d.add(UcompOSDialogueProxy.TEXT_AREA,"Sample Dialogue","Please tell us why you like the UcompOS in 1,000 words or less:");
			d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler);
		}
 
		private function eventHandler(event:SDKEvent):void
		{
			var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy;
			trace(event.data.data);
			d.close();
		}
 
	}
 
}

The TextArea example is almost identical to the TextInput example.

RadioButtons Dialogue Example

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class RadioButtonsDialogue extends Sprite
	{
 
		public function RadioButtonsDialogue()
		{
			SDKClient.getInstance(this);
			var d:UcompOSDialogueProxy = new UcompOSDialogueProxy();
			var choices:Array = ["ActionScript 3","JavaScript","PERL","C++"];
			d.add(UcompOSDialogueProxy.RADIO_BUTTONS,"Sample Dialogue","What is your favorite programming language?",0,0,false,UcompOSDialogueProxy.CANCEL,UcompOSDialogueProxy.SUBMIT,true,true,{choices:choices});
			d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler);
		}
 
		private function eventHandler(event:SDKEvent):void
		{
			var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy;
			trace("Your favorite programming language is: "+event.data.data);
			d.close();
		}
 
	}
 
}

Notice in the example above, we are building an Array of choices to give to the user where a Radio Button will be associated with each choice.  The Array is passed as a property named choices in the initData parameter passed to the UcompOSDialogueProxy‘s add() method.

SWFLoader Dialogue Example

package
{
 
	import flash.display.Sprite;
	import com.ucompass.ucompos.sdk.SDKClient;
	import com.ucompass.ucompos.sdk.proxycomponents.UcompOSDialogueProxy;
	import com.ucompass.ucompos.sdk.events.SDKEvent;
 
	public class SWFLoaderDialogue extends Sprite
	{
 
		public function SWFLoaderDialogue()
		{
			SDKClient.getInstance(this);
			var d:UcompOSDialogueProxy = new UcompOSDialogueProxy();
			var source:String = "http://applications.ucompass.com/MyCustomDialogue.swf":
			d.add(UcompOSDialogueProxy.SWF_LOADER,"Sample Dialogue","",0,0,false,UcompOSDialogueProxy.CANCEL,UcompOSDialogueProxy.SUBMIT,true,true,{source:source});
			d.addEventListener(UcompOSDialogueProxy.DIALOGUE_SUBMIT,eventHandler);
		}
 
		private function eventHandler(event:SDKEvent):void
		{
			var d:UcompOSDialogueProxy = event.target as UcompOSDialogueProxy;
			trace("The Connection Id of your SWF content is: "+event.data.data);
			d.close();
		}
 
	}
 
}

In this case, we are passing the URL of a SWF to the dialogue.  When the user presses the Submit button for the dialogue, the connection id of the SWF content is returned in the SDKEvent dispatched by the UcompOSDialogueProxy instance.

From here, UcompOS SDK transactions could be used to communicate directly with the SWF content and retrieve special information from it.