Managing Singletons in external swfs - actionscript-3

I'm dealing with the scenario whereby my code might be included in other Flash content either included via import .as commands and then referenced as a Singleton, e.g.
import com.as3.Singleton;
...
...
Singleton.birth();
Singleton.getInstance().test();
...but also imported as a runtime library; with the Singleton class exported as a .swf beforehand (instead of pre-baking the class).
How should I reference the Singleton once Event.COMPLETE has fired off from the Loader that brings in the swf? Normally I'd code something like:
public function singletonCompleteHandler(event:Event):void {
var mySing:Singleton = _loader.contentLoaderInfo.content as Singleton;
}
...but I know I don't want to be referencing the singleton via a "var" reference. I'm not explaining very well, but basically once the singleton.swf has loaded in I need to use the code within it within a singleton model (i.e. ensure there's only one instance of it throughout my application).
Copy of the Singleton class included below (thanks for any thoughts on this by the way).
package
{
public class Singleton extends Sprite
{
private static var instance:Singleton;
public function Singleton() {
if (instance) {
throw new Error("Singleton can only be accessed through Singleton.getInstance()");
}
}
public static function birth() {
if (instance == null) {
instance = new Singleton();
}
}
public static function getInstance():Singleton {
return instance;
}
public function test():void {
trace("Testing our singleton.");
}
}
}

First of all, if you're loading it dynamically, then you don't want to have a reference to it in your loading SWF (otherwise it would defeat the point).
So I'm guessing you're looking to do something like this:
function completeHandler(event:Event):void
{
var singleton:Object = loader.contentLoaderInfo.content;
var instance:IMyObject = singleton.getInstance();
instance.test();
}
IMyObject is of course optional here. If you do it like this, your singleton instance will have to implement IMyObject.
interface IMyObject
{
function test():void;
}
This is all to avoid having to reference the actual class in your loading SWF. Like I said, the interface is optional: you can just use Object instead.
... and now on to the main point: load the singleton SWF into the loading SWF's own "application domain".
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/system/LoaderContext.html#applicationDomain
var lc:LoaderContext = new LoaderContext();
lc.applicationDomain = ApplicationDomain.currentDomain;
loader.load(new URLRequest("Singleton.swf"), lc);
You see, normally when you load a SWF, it gets loaded into its own application domain. But this makes it impossible to enforce the singleton pattern on the loaded SWF, because each instance of the class can live in its own application domain (hence you can end up with multiple instances). So if you want to enforce this across multiple SWF loads then you want to load it into the loading SWF's application domain.

If your question is "How should I reference the Singleton once Event.COMPLETE has fired off from the Loader that brings in the swf?", then you can do it with:
var Singleton:Object = _loader.contentLoaderInfo.applicationDomain.getDefinition('Singleton');
But, I'm not sure what you mean about not wanting to use a "var" reference.
On a side-note, there's a good chance a global variable would be a better option than a Singleton class for an API.
package myPackage
{
public var myGlobal:MyGlobal = new MyGlobal();
}
Which you can access with myPackage.myGlobal

Related

Flash loading first external swf loaded

I am making an application to test art from a game I volunteered for. Right now the example I am posting will only touch the armors but the loading process is the same throughout the program. I have a movieclip ready to hold the loaded file but it adds it to the container via the class. It works how it should however my issue is that if you use another file with the same classes then it will default to the first file loaded. Even i use loaderr.unloadAndStop() and remove everything from the stage, it will always load the first file that corresponds to the class I am loading by. Since the armor pieces are loaded by class it makes it a hassle to test multiple changes to an armor file without changing the classes on each export. Here is an example of the code that is being used and I am curious if there is any way that I can improve this. `
public class Test extends MovieClip
{
public var mcChar:Display;
public var btnTest:SimpleButton;
public var btnTest2:SimpleButton;
public var ldr:Loader = new Loader();
public var strSkinLinkage:String;
public var strGender:String;
public function Test()
{
btnTest.addEventListener(MouseEvent.CLICK, TestP);
btnTest2.addEventListener(MouseEvent.CLICK, TestP2);
}
public function TestP(e:MouseEvent)
{
mcChar = new Display();
stage.addChild(mcChar);
mcChar.x = 789.6;
mcChar.y = 604.75;
mcChar.width = 667.15;
mcChar.height = 478.55;
strSkinLinkage = "CNC";
strGender = "M"
this.ldr.load(new URLRequest("CNC.SWF"), new LoaderContext(false, ApplicationDomain.currentDomain));
this.ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
public function TestP2(e:MouseEvent)
{
mcChar = new Display();
stage.addChild(mcChar);
mcChar.x = 789.6;
mcChar.y = 604.75;
mcChar.width = 667.15;
mcChar.height = 478.55;
strSkinLinkage = "CNC";
strGender = "M"
this.ldr.load(new URLRequest("CNC2.SWF"), new LoaderContext(false, ApplicationDomain.currentDomain));
this.ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
public function onLoadSkinComplete(e:Event):*
{
var AssetClass:Class;
try
{
AssetClass = (getDefinitionByName(((strSkinLinkage + strGender) + "Head")) as Class);
mcChar.head.addChildAt(new (AssetClass)(), 0);
}
catch(err:Error)
{
AssetClass = (getDefinitionByName(("mcHead" + strGender)) as Class);
mcChar.head.addChildAt(new (AssetClass)(), 0);
};
AssetClass = (getDefinitionByName(((strSkinLinkage + strGender) + "Chest")) as Class);
chest.addChild(ldr.content (AssetClass)());
mcChar.chest.addChild(new (chest)());
this.ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onLoadSkinComplete);
}
}
`
I don't think its well formatted on this site but this is the core code. I have separate removal functions and my imports are all there. Like I said I cant seem to get it to format correctly. This is my test scenario and isn't my full dynamic tester where I can choose the file. Any help in figuring out how to use the most recent file is appreciated. Also for some background I am more of a self taught novice in as3.
When it gets to loading and unloading assets in AS3, there are several things to learn.
ApplicationDomain is a container for class definitions. The getDefinitionByName(...) method is basically the same as calling the ApplicationDomain.getDefinition(...) on the current ApplicationDomain (or maybe on the main ApplicationDomain, I never tried to do it in the loaded content). As the side result, you cannot have two classes with the same names inside the same ApplicationDomain (or rather you can, but one of them is inaccessible, who knows).
When you load another SWF which falls into the "same domain" category (same www domain, or same/nested local folder), AS3 automatically mixes all the definitions from the loaded SWF into the main ApplicationDomain. If you are willing to have some advanced control over loading/unloading stuff, or/and there are "skin" libraries that have similar sets of classes, you need to put the loaded files into separate ApplicationDomains or their definitions will collide and the result will be unpredictable (yet obviously not satisfactory).
The Loader.load(...) method has a second argument that allows you to do so:
// If there are no mandatory constructor arguments,
// you are free to omit the () brackets. I like doing so.
var aLoader:Loader = new Loader;
var aRequest:URLRequest = new URLRequest("mylibrary.swf");
// Documentation states that passing no argument here is
// the same as passing ApplicationDomain.currentDomain.
var childDomain:ApplicationDomain = new ApplicationDomain;
var aContext:LoaderContext = new LoaderContext(false, childDomain);
aLoader.load(aRequest, aContext);
Thus, when external SWF library is loaded, you can obtain its classes/definitions as following:
var aClass:Class;
// If you get things from the loaded SWF's Library
// then it is Sprite or MovieClip for the most cases.
var anAsset:Sprite;
aClass = aLoader.contentLoaderInfo.applicationDomain.getDefinition("MyAssetClass") as Class;
anAsset = new aClass;
When you do not longer need some of the loaded libraries, you call the Loader.unloadAndStop(...) method on the relevant Loader instance. Combined with the loading SWF into separate ApplicationDomain you can be sure that all of the loaded content (graphics, classes, sounds) is unloaded, destroyed and removed (that one I actually checked):
// Passing "true" also forces the Garbage Collector
// to actually do its job for a change.
aLoader.unloadAndStop(true);

linking fla files together in actionscript using document classes

I am working in actionscript3, and since I'm self-taught, I think I've developed some bad habits, including coding on the timeline and using multiple scenes.
I am hoping to rectify this now that I'm working on a larger project.
Based on what I've read, linking multiple .fla files together is a better practice, each with their own document class. Is that correct?
If so, how do I load one .fla with its document class and then link that into the subsequent .fla file (instead of using scenes)? Or am I misinterpreting what was recommended?
Thanks!
There's no point to split your application in several loadable modules unless you have any of the following preconditions:
you have smart resource management to load and unload content
if you put everything into one file it gets just too big and hard to work with in design time or it takes far too long to compile
Regular AS3 alternative to working with scenes is creating/destroying content instances and using the main document class as their manager. You design content in the library and create behavior AS3 classes for them. Lets say, you have two content classes A and B. At the start the manager should show one of them and wait for the signal to show next one:
private var APage:A;
private var BPage:B;
gotoA();
function gotoA():void
{
if (BPage)
{
BPage.destroy();
removeChild(BPage);
BPage.removeEventListener(Event.CLOSE, gotoA);
}
APage = new A;
APage.addEventListener(Event.CLOSE, gotoB);
addChild(APage);
}
function gotoB():void
{
if (APage)
{
APage.destroy();
removeChild(APage);
APage.removeEventListener(Event.CLOSE, gotoB);
}
BPage = new B;
BPage.addEventListener(Event.CLOSE, gotoA);
addChild(BPage);
}
So, both A and B should have respective methods .destroy() that release used resources, unsubscribes methods from events, remove display objects, and so on, and they both should fire Event.CLOSE when they're done.
If you have many pages like that, you need to go for more algorithmic approach. For example, to create class BasicPage which will interact with manager and have the methods needed in all pages already declared:
package
{
import flash.display.Sprite;
class BasicPage extends Sprite
{
// A reference to the page manager instance.
public var Manager:PageManager;
public function destroy():void
{
while (numChildren > 0) removeChildAt(0);
Manager = null;
}
// Subclasses will have an access to this method to tell manager to show another page.
protected function showOtherPage(pageClass:Class):void
{
Manager.showPage(pageClass);
}
// A method that is called by manager when everything is ready.
// If page should take any actions on start it is a good idea to override this method.
public function startEngine():void
{
}
}
}
Then, example page A:
package
{
import flash.events.MouseEvent;
public class A extends BasicPage
{
// Lets say, class A in library have a designed button named Click.
public var Click:SimpleButton;
// We have things to undo here.
override public function destroy():void
{
Click.removeEventListener(MouseEvent.CLICK, onClick);
Click = null;
// Pass the destruction to superclass so it wraps its existence either.
super.destroy();
}
override public function startEngine():void
{
Click.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(e:MouseEvent):void
{
// Lets use inherited method to show other page.
showOtherPage(B);
}
}
}
So, PageManager will be like:
package
{
public class PageManager extends Sprite
{
private var Page:BasicPage;
// constructor
function PageManager()
{
super();
showPage(A);
}
function showPage(pageClass:Class):void
{
if (Page)
{
Page.destroy();
removeChild(Page);
Page = null;
}
Page = new pageClass;
Page.Manager = this;
addChild(Page);
Page.startEngine();
}
}
}
This all could look scary at first, but it really isn't. PageManager will always have a current page, once there's a need to show another page, the current will be destroyed on a regular basis. Each page class will tend to its own content, which makes coding simpler, for you don't need to see the whole picture. If you need any persistent data, keep it in the PageManager so each page will have access to the data with no need for the pages to communicate with each other.

Can I still create Global variables in AS3

Following the answer here, I have created a file called MyGlobals.as and placed some global variables and functions so that I can access it from anywhere within my project just like AS3 buil-in functions such as trace() method.
This is MyGlobals.as which is located in the src folder (top level folder)
package {
public var MessageQueue:Array = new Array();
public var main:Main;
public var BOOKING_STATUS_DATA:Object;
public function postMessage(msg:Object):void {
MessageQueue.push(msg);
}
public function processMessage():void {
var msg:Object = MessageQueue.pop();
if (msg) {
switch (msg.type) {
}
}
}
Looks like my IDE (FD4) is also recognizing all these functions and variables and also highlighting the varibles and functions just like any other built-in global functions. However, I am getting compilation errors "Accessing possibly undefined variable xxx". The code is as simple as trace(MessageQueue) inside my Main (or another classe).
I am wondering if there was any change Adboe has done recently that it can't be done now or am I missing something? I am not sure if I need to give any special instructions to FD to include this MyGlobals.as?
I am using FD4, Flex SKD 3.1, FP12.0
I am aware of the best practices which suggests to avoid using this type of method for creating global variables but I really need it for my project for my comfort which I feel best way (right now) when compared to take any other path which involves daunting task of code refactoring. I just want do something which can be done in AS3 which I guess is not a hack.
I've done some playing around; it looks like you can only define one (1) property or method at package level per .as file. It must be the same name (case-sensitive) as the .as file it is contained in.
So no, nothing has changed since the older Flash Versions.
In your case that would mean you need five separate ActionScript files along the lines of:
MessageQueue.as:
package
{
public var MessageQueue:Array;
}
main.as:
package
{
public var main:Main;
}
...etc. As you can see this is very cumbersome, another downside to the many others when using this approach. I suggest using the singleton pattern in this scenario instead.
package{
public class Singleton{
private static var _instance:Singleton=null;
private var _score:Number=0;
public function Singleton(e:SingletonEnforcer){
trace(‘new instance of singleton created’);
}
public static function getInstance():Singleton{
if(_instance==null){
_instance=new Singleton(new SingletonEnforcer());
}
return _instance;
}
public function get score():Number{
return _score;
}
public function set score(newScore:Number):void{
_score=newScore;
}
}
}
then iin your any as3 class if you import the singleton class
import Singleton
thn where u need to update the global var_score
use for example
var s:Singleton=Singleton.getInstance();
s.score=50;
trace(s.score);
same thing to display the 50 from another class
var wawa:Singleton=Singleton.getInstance();
trace(wawa.score)

Is there a way to clear out embedded Bitmap assets in AS3/AIR

first time posting on here.
I'm creating an AIR 3.0 app.
For a lot of my graphical assets I'm using the Flex embed metadata to embed bitmap objects as Classes and then instantiating them.
The problem is that it seems these never get garbage collected. I haven't found much info online but I've seen a couple of posts that seem to confirm this.
Anytime one of my classes gets instantiated that has these embedded assets, they always create new instances of the Bitmaps and BitmapDatas rather than reusing what's already in memory. This is a huge problem for memory. And I can't find any way of de-referenciong them or getting them to leave memory.
So the only solution I can think is to just load the graphics from disk rather than using the embed tag. But I'd rather not do this seeing as how when the app is packaged and installed, all of those graphcial assets will be on the end users computer rather than contained within the SWF.
Anyoen run into this? Have a solution? Or an alternate solution than the one I can think of?
Thanks!
Kyle
Well, I guess this is expected behaviour, because the new operator should always create new objects. But those new objects should get garbage collected, just the asset class will not, since it is a class.
You could build a cache that acts like a singleton factory. You request your image by specifying an id, the cache then either creates that image if it doesn't exist already, or just return the single instance if it does. It's been a while since I last coded ActionScript, so maybe you should take this as pseudo-code ;)
public class Cache {
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;
[Embed(source="example.gif")]
public static var ExampleGif:Class;
/**
* The single instance of the cache.
*/
private static var instance:Cache;
/**
* Gets the Cache instance.
*
* #return
* The Cache
*/
public static function getInstance():Cache {
if (Cache.instance == null) {
Cache.instance = new Cache();
}
return Cache.instance;
}
/**
* The cached assets are in here.
*/
private var dictionary:Dictionary
public function Chache() {
if (Cache.instance != null) {
throw new Error("Can not instanciate more than once.");
}
this.dictionary = new Dictionary();
}
/**
* Gets the single instantiated asset its name.
*
* #param assetName
* The name of the variable that was used to store the embeded class
*/
public function getAsset(assetName:String):Object {
if (this.dictionary[assetName] == null) {
var AssetClass = getDefinitionByName(assetName) as Class;
this.dictionary[assetName] = new AssetClass();
}
return this.dicionary[assetName];
}
}
You could then use it like this:
public class Example {
public static function main() {
Bitmap exampleGif1 = Cache.getInstance().getAsset("ExampleGif") as Bitmap;
Bitmap exampleGif2 = Cache.getInstance().getAsset("ExampleGif") as Bitmap;
trace("both should be the same instance: " + (exampleGif1 == exampleGif2));
}
}
I didn't test this, so let me know if it works.
I think what you're looking for is dispose() http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html?#dispose()
If you decide to go with a caching system, here is a link with some code that is tested http://thanksmister.com/2009/01/29/flex-imagecache-a-cheap-way-to-cache-images/ . The link it has to another technique, using SuperImage, is broken, but I managed to find this http://demo.quietlyscheming.com/superImage/app.html .

AS3 event across multiple files

I'm migrating from AS2 to AS3 and have this problem..
In my project I used main document file, which could load multiple nested animations as a separate files. In the main document I had a global function, for example:
_global.onAnimationEnd(mc:MovieClip){...}
and in animations (could be nested) I just called it like:
onAnimationEnd(this);
I'm new with AS3 but somehow thing that the proper way in AS3 is to use Event system, but I have problems to do it. Is there someone who can help with such an easy(AS2) issue?
The way that AS3 handles events is quite different to the AS2 approach. In AS3 events bubble up the object hierarchy and then back down to the originating object. If you have objects further up the hierarchy that are required to respond to an event it is necessary to set up event listeners on the recipient objects to handle the event as it bubbles.
Colin Moock's book Essential ActionScript 3.0 published by O'Reilly deals with it comprehensively. Not much help if you need a quick fix right now though…
Finally I'm using this approach:
I'm using custom event like:
package com.oldes {
import flash.events.Event;
public class GameEvent extends Event {
public var data:Object;
public static var ANIMATION_END = "onAnimationEnd";
public function GameEvent(
type:String,
data: Object,
bubbles:Boolean=true,
cancelable:Boolean=false
){
super(type, bubbles, cancelable);
this.data = data;
}
override public function clone():Event {
return new GameEvent (type, data, bubbles, cancelable);
}
}
}
In my deep nested animations I replaced:
onAnimationEnd(this);
with:
import com.oldes.GameEvent;
dispatchEvent(new GameEvent(GameEvent.ANIMATION_END,{anim: this}));
Using clasic listeners to deal with the cached event.
EDIT: the external file with animations must be properly loaded into the correct context, but that's another AS3 story.