Vic on Flex
Wednesday - May 30, 2007
Flash.media.Sound & Memory Issues in Flex
Taking a short break from my module woes, I've been working with audio in Flex. In my app I was building a preview intro scan of my listed mp3's. What I noticed is that as the list of mp3's got longer, the memory that my browser was eating up got larger. I haven't really found any articles addressing best practices regarding handling audio in Flex, however I would venture a guess that this has been addressed many times in the past by Flash Developers.
I'm pretty tired so I'll keep this simple. Best way I have found to ensure that an audio stream is destroyed and the memory returned when you stop it:
Trick is to declare the variables outside the function, ensure you configure a sound channel (audio) for your sound, and make sure you that you can reference both from the stop function. Then you need to stop the audio, remove any event listeners, and set sound to null. I found it's also a good idea to do your URLRequest inline, as it seems to get destroyed at the same time as mySound.
private var mySound:Sound = new Sound();
private var audio:SoundChannel = new SoundChannel();
private function startPlaying():void{
mySound = new Sound(new URLRequest("music/sample.mp3"));
mySound.addEventListener(Event.COMPLETE, completeSound);
audio = mySound.play();
}
private function stopPlaying():void{
audio.stop();
mySound.removeEventListener(Event.COMPLETE,completeSound);
mySound = null;
}
Short, simple.
Sunday - May 13, 2007
Can Cairngorm and Modules play nice? Sample App
First I'd like to say thanks to those of you that commented on my previous blog entry. I was inspired to go back and take another look at ApplicationDomain. To be honest, I can't get it to work for me. So I'm just going to put up my prototype application with source code and let you play with it. There is a shell application and 3 very simple modules. The challenge is to have a controller in the shell and still have the modules all work independently of each other. The sample app as it sits on the server works because it's essentially Cairngorm free. Good luck!
Source can be found here. Demo can be viewed here.
Saturday - May 12, 2007
Can Cairngorm and Modules play nice?
Let me say, first off, that my wife gave birth to a beautiful baby girl on Wednesday morning, and they are doing just fine. I've been relocated to the spare bedroom so that I can actually function during the day, however I'm missing about 30 hours of sleep this week that I'll never get back. Having said that, I am blogging today because even though I've sort of fixed my latest issues, I'm not entirely sure I've used the best approach.
This is really a sequel to my previous blog... I am still fighting with getting modules to load properly and independently of each other and the shell flex application. Here's the deal...
I have one complex flex module that is built on the Cairngorm 2.2 framework. In itself, the module works fine, and after the progress from the other day, loading multiple instances of the module into a simple shell works fine too. However, when I tried loading multiple instances of the module into the actual application, things died. Why? Because the shell is built on the Cairngorm framework as well. The minute I add in the <services:AppController id="appController"> line to my shell app, subsequent instances of the module don't load properly. I determined that the shell needed to use a different controller than the module, but that wasn't enough (though still necessary). There were funky things happening again when events were triggered - weird casting errors, events firing across multiple module instances, shared models, etc.
A couple of observations:
CairngormEventDispatcher basically returns a singleton. If the shell app gets an instance of this class, then any Modules that load will use the same instance. So, adding the appcontroller to the application will in fact get an instance of the dispatcher. Running through the debugger, I noticed that as I loaded each new instance of the same module, event listeners were added to the singleton's eventDispatcher. So, say your module was listening for the ContactEvent.LOAD_CONTACT event. If you loaded 3 instances of that module, your controller would execute 3 commands, once for each module.
When an event is received by a command, and you try to cast that event back to its origin, i.e. ContactEvent(event), it will only work in the module that actually originated the event. The other modules will throw the following exception:
" cannot convert com.crazedCoders.moduleTest.event::ContactEvent@4b23281 to com.crazedCoders.moduleTest.event.ContactEvent." Trying to test for it, i.e. if(event is ContactEvent) doesn't work since the event is in fact a ContactEvent. Very frustrating.
If you do not use any Caingorm in your shell application, you will not run into these issues, so for some that might be the quick solution. Sadly I was not afforded that luxury, so I had to find another way.
Since I was trying to treat each Module as an autonomous mini-application, I decided that I needed a CairngormEventDispatcher specifically for each Module and the Application. I created a Factory Class that would do just that. Here's the code:
package com.adobe.cairngorm.control{
import mx.modules.ModuleManager;
public class CairngormEventDispatcherFactory
{
private static var instances : Array = new Array;
public static function getDispatcher(obj:Object) : CairngormEventDispatcher
{
// in order for each module developed using cairngorm to be able
// to work independently of the shell and other modules,
// we need to create a hash map of cairngormeventdispatchers
// that are keyed on module instance or application.
// This prevents cross module listening for events,
// which is beneficial especially when loading multiple instances
// of the same module
var ed:Object = ModuleManager.getAssociatedFactory(obj);
var parent:String = "application";
if(ed != null){
trace(ed.name);
parent = ed.name;
}
if(instances[parent] == null){
var cgDispatcher:CairngormEventDispatcher = new CairngormEventDispatcher();
instances[parent] = cgDispatcher;
return cgDispatcher;
}
else{
return instances[parent];
}
}
}
}It took me a while, but eventually I stumbled upon ModuleManager.getAssociatedFactory(object). This function *seems* to be able to figure out within the context of which Module (if any) the object was instantiated. If an object belongs to the shell, it returns null, if not, then it returns a ton of information, including a name property that contains a unique name i.e. instance77 for the loaded module. I create a new instance of the CairngormEventDispatcher for each instance and stuff it in the static associative array, then I replace the usual CairngormEventDispatcher.getInstance().dispatchEvent(...) withCairngormEventDispatcherFactory.getDispatcher(this).dispatchEvent(...));
Now the application shell and each module gets it's own singleton, and the overlap seems to be taken care of.
Again, I welcome any feedback. I'm not particularily fond of this solution, and I hope someone out there can offer a better one.
Monday - May 07, 2007
Loading Multiple Independent Instances of a Module
So, it's Monday night... my wife's contractions are 13 minutes apart, and it seems I have nothing better to do than blog. Of course, this is more akin to the calm before the storm that will be the next 24 hours and probably the next six months. Eventually I will once again emerge from the dark precipice that lies ominously before me. But on to modules.
I love Cairngorm... I just upgraded all my projects 2.2 and it was painless, nothing like child labor, I'm told. I am working on a project where we have to build all these pretty much independent modules, and in these modules we stick to Cairngorm as well.
The first problem we ran into occurred when attempting to load 2 instances of the same module. ModuleManager, as it turns out, keys modules by url. So if you try to load the same module twice, you're just getting a reference to the same one. There is no way around this (or so I thought) This has a number of repercussions:
- Only one ServiceLocator instance can be instantiated - this wonderful little Error message pops up the minute you load a second instance of the module. Quick workaround is to declare the system locator code much like you do model:
private var dataServices:ServiceLocator = ServiceLocator.getInstance();
and abstain from using the mxml format:
<business:Services id="dataServices" /> - All your module instances will share the same model... so if you say do a search, get a searchresult back and stick that in your model, all your modules will display the same search results, which defeats the purpose of having separate file/image search browsers (for example).
Here's a couple other funky things I tried:
- Created 2 modules pretty much identical, Mod1.swf and Mod 2.swf. I loaded them up and they both worked fine and independently
- Created a copy of Mod1.swf and called it Mod1a.swf. I loaded them up and they both worked fine.
- Tried loading two Mod1.swf and suddenly no independence, plus the ServiceLocator error.
I tried wiring up my own ModuleManager, but after a good couple hours I gave up... I was getting all sorts of weird behaviour. I figured out that everything is based around the url that you pass to ModuleManager or ModuleLoader. I was pretty much ready to give up when I had this really silly notion, I did this:
private var count:int;Much to my surprise, this hack worked. The modules loaded successfully and ran independently. Of course, I probably need to test this some more, but I figured I'd blog it quick before the trauma of childbirth hopefully graces me with some convenient and kind short term memory loss.
private var spaces:String = "";
private function LoadModule():void{
for (var x:int; x < count; x++) spaces += " ";
var info:IModuleInfo = ModuleManager.getModule("mod1.swf" + spaces);
info.addEventListener(ModuleEvent.READY, done);
info.load();
count ++;
}
private function done(event:ModuleEvent):void{
var visual:DisplayObject = event.module.factory.create() as DisplayObject;
tabber.addChild(visual);
}
Friday - April 27, 2007
Mapping VO's from Flex to PHP using AMFPHP
I sometimes wonder why it is that I never seem to blog till shortly after the midnight hour... oh well... life just gets too crazy when the sun is up. So as it happens, one of my minions spent the better part of today putting together php classes, using a nifty code generator found called DAO Generator by the guys over at Titantic Linux. It seems to do a pretty decent job of generating the sql necessary to create the db table, and then builds three additional files, the VO, a datasource file, and then finally a DAO file that has a ton of functions in it. (and I mean a ton!)
Going to take a moment here to give credit where credit is due... Michael Ramirez's tutorial on Using Amfphp 1.9 with the Adobe Flex 2 SDK is what really got us started in the right direction, it's pretty detailed and yet easy to follow. We quickly were able to get our AMFPHP up and running with the tutorial files as outlined by Michael. Tickled pink by the code generator we figured we could easily start building our own php gateway. But enough background... on to the thick of things.
Passing VO's from PHP seems to be very well covered in numerous articles on the web, and it involves mainly adding a var $_explicitType="tutorials.Person" to the php VO, with a value that corresponds to the [RemoteClass(alias="tutorials.Person")] in the Flex VO. Pretty straightforward and foolproof. But no one seems to spend any time talking about going back the other way.
Generally when a VO is passed from Flex to AMFPHP, it is treated as an Associative Array. In Michael's examples, he treats the ValueObject that is passed back to Save function as just that $valueObject["lastName"]. But we were pretty keen on using the generated code, which actually uses the syntax $valueObject->getLastName(). And this of course, failed, because the $valueObject being passed to PHP from Flex was not in fact being mapped back to the right VO, or any php VO for that matter.
Turns out, after much googling, that only under certain conditions will AMFPHP successfully map an incoming Flex VO to the corresponding PHP VO. In the globals.php, found at the root of the AMFPHP folder structure, you'll find the following line:
$voPath = "services/vo/";
Meanwhile we had been placing our VO in the same folder as our Datasource and DAO files, something like com/crazedCoders/testapp, and this was off the services/ folder, not the services/vo folder. In fact we didn't even have a /vo folder. I found this little tidbit of information: "As for incoming class mapping, remember that if amfphp doesn't find the class to be mapped in the services/vo folder, it will simply deserialize the object as though it were untyped, that is, as an associative array. " in this blog.
It took a little bit of playing around but ultimately, without making any changes to the core AMFPHP files, I was able to get the reverse mapping working successfully. This is what that looked like:
//file: src/tutorials/Person.asand then in php:
package tutorials{
[RemoteClass(alias="tutorials.Person")]
[Bindable]
public class Person {
public var firstName:String;
public var lastName:String;
public var phone:String;
public var email:String;
}}
<?phpTo test the mapping I added the following function to a PersonService.php file:
//file: services/vo/tutorials/Person.php
class Person {
var $firstName;
var $lastName;
var $phone;
var $email;
// explicit actionscript package
var $_explicitType = "tutorials.Person";}
function formatLastName(){
$lastName = strtolower($lastName); }
?>
//file: services/tutorials/PersonService.phpOnly if the valueobject was mapped correctly would it be able to call the function on the PHP VO. In Flex I just created a Person object, passed it in to the remoteobject call, and observed the formatting of the last name on the Person object that was returned.
function modify($person){
//this will call a function on person and return it
$person->formatLastName();
return $person;
}
Again, I hope this will save others the time that it took me to figure this out. Enjoy!








