Flex Internals

Welcome to Flexoland. First of all I am not evangelist or big "fan" of ActionScript/Flex, althought it implements interesting ideas. My fields of interest are C++/C# Win32/WinFX system/IE/application development, sometime with interruptions for Java and misc scripting. I'd be interested in robot-building, AI systems, high performance servers but this is out of scope of real work now. Latest RIA projects I was involved in were based on Ajax/Flex platforms. This page is place for strange and interesting things found in this environment (Flex 1.5/2/3, ActionScript 2/3).

P.S.: NO WARRANTIES ARE EXTENDED. USE ALL DESCRIBED IDEAS AT YOUR OWN RISK. Some ideas might be obsolete, as Adobe periodically updates Flex. Also please forgive me my bad English and be happy.


Content


getClassByName

ActionScript 2.0 doesn't provide API like flash.utils.getClassByName. There is also no built-in "Class" type, and only [ClassReference] attribute used to mark property/field as class reference member. Good news is that getClassByName can be simulated using "eval" function:


 public static function getClassByName(className:String) { return eval("_global."+className); }


How to list all loaded classes

Intersting enough, that AS2 stores all class references as fields of _global object, replacing '.' width '_' (I'd also be happy to know if AS3 somehow provides some special storage for all loaded classes, and the way to access it, in my understanding it's going to be somewhere inside ApplicationDomain):


 public static function listClasses()
 {
  _global.ASSetPropFlags(_global, null, 0, true);
  for(var name in _global)
  {
   var v = _global[name];
   if( v==null || typeof(v)!="function" )
    continue;

   if( v["__proto__"]==null ) continue;

   var clsName = name.split("_").join(".");
   Logger.trace("Found class: "+clsName+", ref="+v);
  }
 }


Parse Date string

Flex 2.0 provides API to parse date string like Date.parse, but I was unable to find something similar in AS2. So I've used mx.formatters.DateFormatter.parseDateString method.


 var s:String;
 ...
 var d:Date = mx.formatters.DateFormatter.parseDateString(s);

Note: the method imlementation is very buggy, doesn't provide localization support and also is not public in Flex 2 anymore.


Accessing proxied object

Under some circumstances Flex wraps object with special mx.utils.ObjectProxy wrapper class. Documentation describes special object_proxy "object" property containing reference to real object. But accessing this property at runtime always returns "null", althought this property is visible in debuger watch window with "package" icon. Code below demonstrates the problem, and shows "Real object: null" message:


 import mx.utils.ObjectProxy;
 import mx.controls.Alert;
 ...
 var o1:Object = { f1:"aaa", f2:123 };
 var o2:Object = new ObjectProxy(o1);
 var o3:Object = ObjectProxy(o2).object;
 Alert.show("Real object: "+o3);

Matt Chotin pointed me to the solution. It's special namespaced property and need in special code handling. So updated example listed below:


 import mx.utils.ObjectProxy;
 import mx.utils.object_proxy;
 import mx.controls.Alert;
 ...
 var o1:Object = { f1:"aaa", f2:123 };
 var o2:Object = new ObjectProxy(o1);
 var o3:Object = ObjectProxy(o2).object_proxy::object;
 Alert.show("Real object: "+o3);



Dynamic method invocation

Sometimes you need to dynamically invoke certain object method in Flex environment. With Flex 1.5 we used the following line to call remote object method at runtime:


 var ro:mx.remoting.RemoteObject;
 ... // somehow initialize it
 ro["foo1"].apply(ro, ["arg1", "arg2"]);

 // code above is equivalent of MXML code like
 // <mx:RemoteObject id="ro2" .../>
 // <mx:Button label="Run" click="ro2.foo1('arg1', 'arg2')" .../>

With Flex 2.0 (ActionScript 3) this solution doesn't work anymore. Our sample will throw exception. It's caused be the fact mx.rpc.remoting.mxml.RemoteObject is derrived from flash.utils.Proxy class and uses it to handle dynamic method invocation tasks, like old Object._resolve/addProperty APIs in ActionScript 2.0. (strictly speaking it's implemented inside mx.rpc.AbstractService class as callProperty/ getProperty/ nextName/ nextNameIndex/ nextValue/ setProperty members). Code fails because ro["foo1"] statement will invoke AbstractService:getProperty() API, althought real ro.foo1(...) statement under the hood invokes callProperty method. And AbstractService:callProperty implementation differs from AbstractService:getProperty one, by calling Operation:send. Actually send() method call need to be moved to the Operation implementation, and possibly ??? it's bug in RPC (for someone interested ("inter esse") in please see disassembled ActionScript byte code (ABC) for these methods). Fortunately we can force callProperty call for proxied objects, as shown below:


 import mx.utils.*;
 import flash.utils.*;

 public static function applyMethod(obj:*, name:String, argArray:Array=null):*
 {
  if( obj is flash.utils.Proxy )
  {
   var args:Array = [name];
   if( argArray!=null && argArray.length>0 )
    args["push"].apply(args, argArray);
   return flash.utils.Proxy(obj).flash_proxy::callProperty.apply(obj, args);
  } else
   return obj[name].apply(obj, argArray);
 }

 ...

 var ro : mx.rpc.remoting.mxml.RemoteObject;
 ...
 applyMethod(ro, "foo1", ["arg1", "arg2"]);
 

Now we can w/o problem implement 1-line "call" method like:


 public static function callMethod(obj:*, name:String, ... args):*
 {
   return applyMethod(obj, name, args);
 }
 ...
 callMethod(ro, "foo1", "arg1", "arg2");


When you only need in dynamic RemoteObject object method call, and don't want to use applyMethod API, the following code will work:


 var method:String = "foo1";

 // static arguments
 ro[method].send(arg1, arg2);

 // dynamic arguments
 ro[method].send.apply(null, [arg1, arg2]);



Stack dumper

There is no API to walk stack in AS3. Sometime this information is useful when spelunking in Flex code. Yes, it's possible to see call stack under debugger. But it could be a problem for complex applications, or possibly this code is hard to catch in debugger at all (like drag and drop, etc.). Below is a simple approach to trace current stack, but it's only limited to debug Flash player version (flash.system.Capabilities.isDebugger is true):


 public static function dumpStack():String
 {
  var res:String = "Stack not available";
  try {
    var o:Object = null;
    o.aaa();
  } catch(e:Error) {
    var s:String = e.getStackTrace();
    var index:Number = s.indexOf("dumpStack");
    if( index<0 ) return res;
    index = s.indexOf("\n", index);
    if( index<0 ) return res;
    return s.substring(index+1, s.length);
  }
  return res;
 }

 ...

 Logger.trace("[STACK] "+dumpStack());

See also Logger @ XPanel tool.



thisObject & Function.call/apply

AS3 Function object provides "call" and "apply" APIs for special function treatment. Both methods accept "thisObject" argument. Documentation says something like: thisObject:Object — The object to which myFunction is applied. It doesn't describe well that thisObject parameter now ignored almost in all cases, in contrast to AS2. By default when you access object method or global function in AS3 code you always get "thunked" object (something like delegate in .NET world or thunks in C++, in other words real function pointer+"this" context pointer). [I'd also glad to know how to extract real function pointer from this pointer? Is it somewhere inside Object.prototype?] e.g. consider the following example:


public class A { public function foo() { trace(""+this); } }
...
var a:A = new A();
var b:String = "I am b";

a.foo(); // -> [object A] OK, it's good
trace(b); // -> I am b OK, it's also good
a.foo.apply(b); // -> [object A] Surprise, it's not good (DM)


The same situation with global function, thisObject is always ignored!!! At this moment I know only one possible case when it works - anonymous function, or function "as is":


var foo:Function = function() { trace(""+this); }
var a:A = new A();
var b:String = "I am b";

foo(); // -> [object global] , OK
foo.apply(a); // -> [object A] , OK
foo.apply(b); // -> I am b , Great it works!

Just FYI.



applyConstructor

AS2 constructor was plain function object, and it was possible to call it using the same technique as other methods. e.g.:


// Flex 1.5 code

var cls = A; // A class reference var o:Object = new Object();
o.__proto__ = cls.prototype;
o.__constructor__ = cls;
var a = cls.apply(o, args); // creating A class and passing args to constructor


Now it doesn't work anymore in Flex2. AS3 defines new Class type, but forgets to add something like Class.create(args:Array) API, which is equivalent of Function.apply method. It's pity. "Object.prototype.constructor" points to constructor function, but sounds like it accepts parameter with raw uninitialized class (just my assumption). But in case when you still need in this functionality, the trick listed below could be used. Thanks to Function.apply! To demonstrate this technique arguments count are limited to 2, but you are free to extend it to as many as you want (actually it's limited by maximum arguments count to function supported by mxmlc compiler or maximum generated block size or SWF size or something else).


public static function applyConstructor0(cls:Class):*
{
  return new cls();
}

public static function applyConstructor1(cls:Class, o1:*):*
{
  return new cls(o1);
}

public static function applyConstructor2(cls:Class, o1:*, o2:*):*
{
  return new cls(o1, o2);
}

private static var s_mapConstr:Array = null;

public static function applyConstructor(cls:Class, args:Array=null):*
{
  if( s_mapConstr==null )
  {
   s_mapConstr = [
   applyConstructor0,
   applyConstructor1,
   applyConstructor2
   ];
  }

  if( args!=null && args.length>s_mapConstr.length )
  {
   trace("Only up to "+s_mapConstr.length+" arguments supported in constructor call.");
   return null;
  }

  var args2:Array = [cls];
  var index:int = (args==null)?0:args.length;
  if( index>0 )
  args2["push"].apply(args2, args);
  return s_mapConstr[index].apply(null, args2);
}



This problem also exists with "super" constructor call. It doesn't provide apply functionality, and you will get the same problem invoking superclass constructor, e.g. when superclass constructor accepts ... rest parameter. Probably the similar approach will work, in combination with #include. E.g. create code snip with if/else or case/switch statement, checking arguments count and invoking appropriate super() constructor, then just include the code snip using #include directive!

XML.setNotification

ActionScript 3 runtime provides interesting undocumented callback to intercept E4X XML events: XML.setNotification(callback:Function). Internally it's used by XMLListCollection class to hook up change events. Also it's unclear for me why this function is undocumented in "XML era".

Callback notification function has the following format:


	function callback(targetCurrent:Object, command:String, target:Object, value:Object, detail:Object):void;

Possible "command" parameter values based on my exepriments and Tamarin source code are:

BTW XMLListCollection Adobe Flex 2.0.1 implementation doesn't intercept all possible events, so it's potentially buggy. More detailed information about the callback parameters can be obtained from my sample listed below and traces.

Simple program to test:


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	creationComplete="doTest()">
	
<mx:Script><![CDATA[

public function doTest():void
{
	var x:XML = <foo/>;
	x.setNotification(xml_notifier);
	
	x.@a1 = "halo";
	x.@a1 = "yelo";
	delete x.@a1;
	
	x.appendChild(<child1/>);
	x.child1 = "some text";
	x.child1[0].setName("child2");
	x.child2 = <child3/>;
	delete x.child3[0];
	
	x.setNamespace(new Namespace("dummy"));
}

private var m_seq:int = 1;
private function xml_notifier(targetCurrent:Object, command:String, target:Object, value:Object, detail:Object):void
{
	trace("["+m_seq+"] command="+command+", targetCurrent="+targetCurrent+", target="+target+", value="+value+", detail="+detail);
	m_seq++;
}
		
]]></mx:Script>	
</mx:Application>

Console output:


[1] command=attributeAdded, targetCurrent=, target=, value=a1, detail=halo
[2] command=attributeChanged, targetCurrent=, target=, value=a1, detail=halo
[3] command=attributeRemoved, targetCurrent=, target=, value=a1, detail=yelo
[4] command=nodeAdded, targetCurrent=<foo>
  <child1/>
</foo>, target=<foo>
  <child1/>
</foo>, value=, detail=null
[5] command=textSet, targetCurrent=<foo>
  <child1>some text</child1>
</foo>, target=some text, value=some text, detail=null
[6] command=nameSet, targetCurrent=<foo>
  <child2>some text</child2>
</foo>, target=some text, value=child2, detail=child1
[7] command=nodeChanged, targetCurrent=<foo>
  <child3/>
</foo>, target=<foo>
  <child3/>
</foo>, value=, detail=some text
[8] command=nodeRemoved, targetCurrent=, target=, value=, detail=null
[9] command=namespaceSet, targetCurrent=, target=, value=dummy, detail=null

AbstractService.operationClass

Note: this code works only for Flex 2.0 prior Flex 2.0.1 Hotfix 2/LCDS 2.5.1 with updated RPC library. Some interesting info for people implementing custom web services ala Flex RPC style. Probably this tip will be unnecessary after Adobe officially publish RPC source code, but meanwhile... RemoteObject and WebService classes are derived from mx.rpc.AbstractService one. Actually AbstractService class provides nice helpers to work with webservice methods. There is "AbstractService.getOperation" API to retrieve certain operation, but it's marked as final and is not allowed to be overridden. OK, it's possible to override Proxy methods like callProperty/getProperty, etc. But.. wait, there is AbstractService.mx_internal:operationClass undocumented property, it's class reference for Operation class. In other words just provide own class there and AbstractService will make all magic for you:

Service class:


package
{
	import mx.rpc.AbstractService;
	import mx.rpc.AbstractOperation;
	
	import mx.core.mx_internal;
	use namespace mx_internal;

	public dynamic class MyWebService extends AbstractService
	{
		public function MyWebService():void
		{
			operationClass = MyOperation;
		}
	}
}


Operation class:

 

package
{
	import mx.rpc.AbstractService;
	import mx.rpc.AbstractOperation;

	public dynamic class MyOperation extends AbstractOperation
	{
		public function MyOperation(svc:AbstractService, name:String = null):void
		{
			super(svc, name);
		}

		override public function send(... args):AsyncToken
		{
			// Place to provide specific to MyWebService logic
			trace("call "+name+"(args="+args+")"); 
		}

	}

}


Service usage:


	var ws:MyWebService = new MyWebService();
	ws.someMethod(1, 2, 3); // <- will dump something like "call someMethod(args=[1,2,3])"

AbstractService.getOperation

Note: this code only works with Flex 2.0.1 Hotfix 2/LCDS 2.5.1 with updated RPC library. Adobe developers removed "final" keyword from "getOperation" method, and removed "operatonClass" member as well. Thank you as it's first step to make the code more transparent and convenient for inheritance/subclassing. I'd be happy if AbstractOperation.flash_proxy:getProperty will be revised as well. As it makes a lot of evil in subclassing tasks. As you might know AS3 proxy functionality (flash.utils.Proxy) is not designed well by design and together with strange AbstractOperation.flash_proxy:getProperty implementation introduces some weird problems. Frankly speaking "getProperty" is very optimistic and supposes that requested property is always RPC operation, but under some circumstances it can be any of RemoteObject members. Also for all functions getProperty should return Function object instance, and this should be rule for all operation as well. And AbstractService should provide clever getProperty implementation allowing if necessary execute AbstractService.super.getProperty API. Make getOperation name parameter as QName, etc

Returning to operation overriding/subclassing: now it's very simple, just override "getOperation" and return necessary Operation class, e.g.:


package
{
	import mx.rpc.AbstractService;
	import mx.rpc.AbstractOperation;
	
	public dynamic class MyWebService extends AbstractService
	{
		override public function getOperation(name:String):AbstractOperation
		{
			var o:AbstractOperation = super.getOperation(name);
			if( o==null )
			{
				o = new Operation(this, name);
				_operations[name] = o;
				o.asyncRequest = asyncRequest;
			}
			return o;
		}
	}
}

Operation class:


package
{
	import mx.rpc.AbstractService;
	import mx.rpc.AbstractOperation;

	public dynamic class MyOperation extends AbstractOperation
	{
		public function MyOperation(svc:AbstractService, name:String = null):void
		{
			super(svc, name);
		}

		override public function send(... args):AsyncToken
		{
			// Place to provide specific to MyWebService logic
			trace("call "+name+"(args="+args+")"); 
		}

	}

}

Service usage:


	var ws:MyWebService = new MyWebService();
	ws.someMethod(1, 2, 3); // <- will dump something like "call someMethod(args=[1,2,3])"

Dynamic events

Just my implementation of dynamic events. Sometimes it's more convenient to use single event class with "dynamic" properties instead of creating custom Event based classes for each type of event. I'd be happy if Adobe includes some dynamic event implementation into framework to prevent each developer create own implementation (Note: in Flex 3, mx.events.DynamicEvent provides correct implementation for clone method). Source code listed below works well with events sent between different modules/application domains. Keypoint is "clone" method, as sometime event dispatcher send cloned version instead of original one (event redispatching). Note: current "copy" method implementation is not well designed for performance, but it works:


package common
{
	import flash.events.Event;
	
	public dynamic class EventX extends Event
	{
		public function EventX(type:String, bubbles:Boolean = false, cancelable:Boolean = false):void
		{
			super(type, bubbles, cancelable);
		}
		
		public override function clone():Event
		{
			return Event(copy(type, bubbles, cancelable));
		}
		
		public function copy(type:String, bubbles:Boolean = false, cancelable:Boolean = false):EventX
		{
			var evt:EventX = new EventX(type, bubbles, cancelable);
			
			// skip standard props
			var std:Object = {};
			for(var p1:String in evt)
				std[p1] = true;
				
			for(var p2:String in this)
			{
				if( std[p2]!=null ) continue;
				evt[p2] = this[p2];
			}
			
			return evt;
		}
		
	}
}

[Transient] metadata flag

Sometimes it's usefull to hide some AS object public R/W properties from AMF serialization process. Similar functionality implemented in MS world for many years before, (e.g. [XmlIgnore] in .NET XML serialization). Now it's possible to control the same statically from AS3:
package
{
	[RemoteClass(alias="FooVO")]
	public class FooVO
	{
		// public field
		public var prop1:String;

		// hidden field
		[Transient]
		public var prop2:Array;
	}
}
Areas of applying - all places using AMF encoding, e.g. RemoteObject, NetConnection, ObjectUtil.copy, etc.

ActionScript 3 VM source code aka "Tamarin"

Many thanks for Alex Harui for enlightening me about AS3 VM source code, it's known as Tamarin open source project now:

 Home page: http://www.mozilla.org/projects/tamarin/
 Source code: http://hg.mozilla.org/tamarin-central/

I always was interested in "playerglobal.swc" objects native implementation, this project provides one more crumb in these fields.

Nemo 440

My free time (25th hour in day), created simple toy, useful in underwater digging. Program prototype in studio!:

 Nemo 440 - advanced ActionScript 3/ABC2/Flex 2/Flex 3/AIR disassembler.

Unhandled errors handler

Flex/AS/Flash were always missing global hook for unhandled errors like SetUnhandledExceptionFilter in Win32, Application.ThreadException in .NET etc. But Flex 3 framework introduced new functionality to intercept some subset of these errors. Usage sample listed below, it's hardcoded to always invoke debugger in case of unhandled error:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application 
	xmlns:mx="http://www.adobe.com/2006/mxml" 
	initialize="constructor()"
>

<mx:Button label="Error" click="callLater(doError)"/>
<mx:Button label="Still not supported" click="doError()"/>

<mx:Script><![CDATA[
import mx.core.UIComponentGlobals;
import mx.controls.Alert;

private function constructor():void
{
	UIComponentGlobals.catchCallLaterExceptions = true;
	Application.application.systemManager.addEventListener("callLaterError", this_callLaterError);
}

private function doError():void
{
	throw new Error('Dummy error');
}

private function this_callLaterError(evt:*):void
{
	Alert.show("Unhandled error: "+evt.error);
	var bypassToDebugger:Boolean = true;
	if( bypassToDebugger )
		throw evt.error;
}
		
]]></mx:Script>
</mx:Application>

Flex 3 + Flex 2 = 0 ?

Recently I needed to host Flex 2.0 SWF application movie inside Flex 3.0 one. First attempt to use SWFLoader introduced many runtime errors. So I created simple class for this purposes. Main idea was to specify separate application domain for Flex 2 application to prevent runtime/framework classes interference with Flex 3 implementation. May be this problem is fixed or described already, but unfortunately I didn't find solution at the moment of writing.

package
{
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import mx.controls.SWFLoader;
	
	public class Flex2Loader extends SWFLoader
	{
		public function Flex2Loader()
		{
			loaderContext = new LoaderContext(false, new ApplicationDomain(null));
			scaleContent = false;
			addEventListener("resize", updateSize);
			addEventListener("complete", this_complete);
		}
		
		private function updateSize(... rest):void
		{
			if( content!=null )
				Object(content).setActualSize(width, height);
		}
				
		private function this_complete(evt:*):void
		{
			if( content!=null )
				content.addEventListener("applicationComplete", updateSize);
		}
	}
}

Usage sample:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
 	xmlns:local="*"
	xmlns:mx="http://www.adobe.com/2006/mxml" 
>
	<local:Flex2Loader width="100%" height="100%" autoLoad="true" source="foo.swf" />
</mx:Application>

Note: To prevent any cross-domain security problems, ensure that hosted application (in our example - foo.swf) listed container domain as trusted one. In other words executes "Security.allowDomain('*')" on early initialization stage ('*' - is bad example from security perspective, and essential for development).

How to determine Flash container type?

Sometimes it's necessary to know Flash player container type used to execute current Flex code. This simple question was asked on Yahoo groups - How do you detect AIR vs Flash Player at runtime? Quick answer is to check "flash.system.Security.sandboxType" or "flash.system.Capabilities.playerType" properties depending on situation. More information is available in AS3 documentation.

Object type equality

All we know "is" AS3 operator. Recently found in Adobe source code one interesting way to check object type equality. I don't know if this is legal way, but at least it works with Flex 2/3:


public class A {}
	...
public class B extends A {}
	...

private function typeEquals(o:*, cls:Class):Boolean
{
	return o==null?false:Object(o).constructor==cls;
}	 

private function test():void
{
	var a:A = new A();
	var b:B = new B();
	var c:C = new C();
	
	trace("typeEquals(a, A) "+typeEquals(a, A));
	trace("typeEquals(a, B) "+typeEquals(a, B));
	trace("typeEquals(b, A) "+typeEquals(b, A));
	trace("typeEquals(b, B) "+typeEquals(b, B));
	trace("a is A "+(a is A));
	trace("a is B "+(a is B));
	trace("b is A "+(b is A));
	trace("b is B "+(b is A));
}



/*
	Output:

	typeEquals(a, A) true
	typeEquals(a, B) false
	typeEquals(b, A) false
	typeEquals(b, B) true
	a is A true
	a is B false
	b is A true
	b is B true

*/

Tracing data services performance MPI Data

Just quick steps how to add client-side logging functionality for Flex Data Services in BlazeDS/LCDS. Complete documentation about measuring performance in BlazeDS/LCDS services described in Adobe Measuring Message Processing Performance document.

1) Enable MPI functionality in BlazeDS/LCDS. Edit channel definitions you want to trace in /WEB-INF/flex/services-config.xml file and add "record-message-times"/"record-message-sizes" options :


...
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
	<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
	<properties>
		<record-message-times>true</record-message-times>
		<record-message-sizes>true</record-message-sizes>
	</properties>
</channel-definition>
...

2) Configure logger, e.g:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="constructor()">
<mx:Script><![CDATA[

import mx.logging.Log;
import mx.logging.LogEventLevel;
import mx.logging.targets.TraceTarget;

private function constructor():void
{
	var t:TraceTarget = new TraceTarget();
	t.level = LogEventLevel.DEBUG;
	Log.addTarget(t);
}
]]></mx:Script>
</mx:Application>

3) See all traces in Eclipse console window, (not only MPI ones that listed below):


...
Original message size(B): 654
Response message size(B): 566
Total time (s): 0.969
Network Roundtrip time (s): 0.953
Server processing time (s): 0.016
Server non-adapter time (s): 0.016
...

4) Use "mx.messaging.messages.MessagePerformanceUtils" class for any custom preformance data processing:


private function messageHandler(message:IMessage):void
{
	var m:MessagePerformanceUtils = new MessagePerformanceUtils(message);
	trace("[messageHandler] "+m.prettyPrint());
	...
}

/*
	Output:

	[messageHandler] Original message size(B): 469
	Response message size(B): 965
	Total time (s): 0.016
	Server processing time (s): 0.031
	Server non-adapter time (s): 0.031
	PUSHED MESSAGE INFORMATION:
	Total push time (s): 1.688
	Originating Message size (B): 521
	Server poll delay (s): 1.641
	
*/

Design-time mode

With Flex Builder 3 Adobe team moved MXML designer in direction closer to real code. According to framework sources it was started in FB2, but somehow amount of user AS3 code executed in designer for Flex 2 was almost 0. Based on my results with FB3 now components AS3 code can be executed in IDE designer, but component has to be declared in separate project (e.g. library). Note: IDE often won't detect changes immediately, so you will need to completely rebuild project and restar Eclipse to see changes in designer. Also not all code executed at design time at all, (e.g. binding code usually ignored).

mx.core.UIComponentGlobals class provides designMode static property to control component behaviour. With help of this flag now it's possible to execute specific to designer code only at design-time and prevent real runtime code from execution in IDE. UIComponentGlobals.designMode declared as:

/**
      *  A global flag that can be read by any component to determine
      *  whether it is currently executing in the context of a design
      *  tool such as Flex Builder's design view.  Most components will
      *  never need to check this flag, but if a component needs to
      *  have different behavior at design time than at runtime, then it
      *  can check this flag.
  */
    public static function get designMode():Boolean
    {
            return mx_internal::designTime;
    }

Light reboot

Created simple code example that demonstrates how it's possible to restart AIR application on the fly. The same functionality is possible with help of well known browser API SWF from Adobe, but this sample is even simpler - no need for external SWF at all:

reboot.as:

package
{
	import mx.core.Application;
	import mx.core.WindowedApplication;
	import adobe.utils.ProductManager;

	public function reboot():void
	{
		var app:WindowedApplication = WindowedApplication(Application.application);
		var mgr:ProductManager = new ProductManager("airappinstaller");
		mgr.launch("-launch "+app.nativeApplication.applicationID+" "+app.nativeApplication.publisherID);
		app.close();
	}
}

lightReboot.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Button label="Reboot me" click="reboot()"/>
</mx:WindowedApplication>

Ensure "allowBrowserInvocation" option is turned on in AIR application descriptor template:

<allowBrowserInvocation>true</allowBrowserInvocation>

And finally compiled demo application: lightReboot.air

Discover AIR Publisher ID on Windows

AIR Publisher ID is necessary when working with browser API. "nativeApplication.publisherID" property provides this information at runtime. Alternative way to discover this information in Windows without source code modification described below:

1) Install certain AIR application.
2) Go to installation folder and locate "\META-INF\AIR\publisherid" file. E.g. for previous app it's "C:\Program Files\lightReboot\META-INF\AIR\publisherid"
3) See content of "publisherid" text file, this is Publisher ID. E.g. lightReboot Publisher ID is "E15D8FFF9A3B1300D0966192A76DCB374DC2ACDE.1".

Have a fun.


Revision:15, Last Modified: 06/11/08 00:05 AM, Copyright © Vadim Melnik 2005-2008. Visit my Homepage.