Flex Background Worker disposing of failed MessageChannel freezes application - actionscript-3

I have the following scenario: I am using a MessageChannel to communicate with a background worker in a flex application. Since the code is to eventually be transformed into a library, it should also be able to handle malformed input (e.g. sending classes via the channel for which no class alias is registered in the background worker). In this case I want to abort the worker. My code is the following:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.system.MessageChannel;
import flash.system.Worker;
public class BW extends Sprite
{
/** Incoming channel */
private var fromCoordinatorChannel:MessageChannel;
/** Outgoing channel */
private var toCoordinatorChannel:MessageChannel;
public function BW()
{
super();
initChannels();
}
/**
* Get channels from shared property and attach event listener
*/
private function initChannels():void {
// Get channnels from shared property
fromCoordinatorChannel = Worker.current.getSharedProperty("toWorkerChannel");
toCoordinatorChannel = Worker.current.getSharedProperty("fromWorkerChannel");
// Attach event listener for incoming messages
fromCoordinatorChannel.addEventListener(Event.CHANNEL_MESSAGE, onIncomingMessage);
}
/**
* Event handler for incoming messages on the channel.
* #param event Event that came in
*/
private function onIncomingMessage(event:Event):void {
handleIncoming();
}
/**
* Get oldest message from channel and handle it
*/
private function handleIncoming():void {
if(fromCoordinatorChannel.messageAvailable) {
try {
var wm:Object = fromCoordinatorChannel.receive(true);
} catch(e:Error) {
fromCoordinatorChannel.close();
trace("Invalid type of package sent - could not be deserialized.");
// Kill myself
fromCoordinatorChannel = null;
toCoordinatorChannel = null;
Worker.current.setSharedProperty("toWorkerChannel", null);
Worker.current.setSharedProperty("fromWorkerChannel", null);
Worker.current.terminate();
}
}
}
}
}
And in the primordial worker:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
var worker:Worker;
var to:MessageChannel;
var from:MessageChannel;
var graveyard:Array = new Array();
private function removeWorkerIfFailed():void {
if(worker && worker.state == WorkerState.TERMINATED) {
from.close();
worker = null;
// What the actual f***? If I allow this channel to be garbage collected, it breaks. If I prevent that, it doesn't (o.Ó)
graveyard.push(to);
to = null;
from = null;
}
}
protected function button1_clickHandler(event:MouseEvent):void
{
registerClassAlias("Example", Example);
// Create worker and channels
worker = WorkerDomain.current.createWorker(Workers.BW);
to = Worker.current.createMessageChannel(worker);
from = worker.createMessageChannel(Worker.current);
// Attach event listener to status of worker so its reference can be deleted when it fails
worker.addEventListener(Event.WORKER_STATE,function(event:Event):void {removeWorkerIfFailed();});
// Set shared properties so worker can access channels
worker.setSharedProperty("toWorkerChannel", to);
worker.setSharedProperty("fromWorkerChannel", from);
// Attach event listener for incoming messages
from.addEventListener(Event.CHANNEL_MESSAGE, function(event:Event):void { trace('incoming'); });
// Start the worker
worker.start();
var example1:Example = new Example("one");
to.send(example1);
}
]]>
</fx:Script>
<s:Button label="Do it" click="button1_clickHandler(event)">
</s:Button>
</s:WindowedApplication>
Add the Example class
package
{
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;
public class Example implements IExternalizable
{
public var name:String;
public function Example(name:String)
{
this.name = name;
}
public function readExternal(input:IDataInput):void
{
name = input.readUTF();
}
public function writeExternal(output:IDataOutput):void
{
output.writeUTF(name);
}
}
}
The problem is the following: If I remove the line in the removeWorkerIfFailed() that pushes a reference to the array (thereby preventing the channel from being garbage collected), the main application freezes. The debugger does not show any active function calls. As long as that line is there, everything works fine.
To reiterate: I know that in order to fix it, I need to call the registerClassAlias(...) also in the background worker, but I am trying to handle precisely this case that someone throws something wrong at the background worker.
Thank you

Related

Custom events not working in AS3

I have an event called SelectEvent. Whenever I click the buyer, the select event should be launched. But that is not what is happening.
My code of base:
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
public class FashionFrenzy extends MovieClip
{
public var Buyer_mc:Buyer;
public var Buyers:Array;
private var BuyerNumber:Number;
public var xpositions:Array;
public var ypositions:Array;
public var SelectedBuyer:Number;
public function FashionFrenzy()
{
GameTimeController();
xpositions=new Array();
xpositions.push(523,563,603);
ypositions=new Array();
ypositions.push(377,377,377);
Buyers = new Array ;
BuyerNumber=0;
Time_mc.gameTimer.addEventListener(TimerEvent.TIMER,GenerateBuyers);
addEventListener(SelectEvent.BUYERSELECT,showselectbuyer);
}
public function GameTimeController()
{
Time_mc.StartGame();
}
public function showselectbuyer(event:SelectEvent):void
{
trace("Bamba");
}
public function GenerateBuyers(event:TimerEvent):void
{
if(BuyerNumber<6)
{
if (Math.random() < 0.01)
{
var position:Number = Math.floor(Math.random()*3);
var newBuyer_mc = new Buyer(xpositions[position],ypositions[position],BuyerNumber);
ypositions[position]-=40;
Buyers.push(newBuyer_mc);
addChild(newBuyer_mc);
BuyerNumber++;
}
}
for each (var buyer_mc:Buyer in Buyers)
{
if(buyer_mc.walking==true)
{
buyer_mc.Enter();
}
}
}
}
}
My code of the buyer:
package
{
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
public class Buyer extends MovieClip
{
public var buyerTimer:Timer;
public var walking:Boolean;
public var stopposition:Number;
public var buyerCode:Number;
public function Buyer(startx:Number, stopy:Number, code:Number)
{
x=startx;
stopposition=stopy;
walking=true;
buyerCode=code;
BuyerProperties();
}
public function Enter():void
{
if(y>stopposition)
{
walking=false;
StartFunction();
}
else
{
y= y+3;
}
}
public function BuyerProperties():void
{
buyerTimer = new Timer( 25 );
trace(" I am generated");
}
public function StartFunction():void
{
buyerTimer.start();
addEventListener(MouseEvent.CLICK,Select);
trace("My timer starts now within Buyer.as");
}
public function Select(event:MouseEvent):void
{
trace(buyerCode);
dispatchEvent( new SelectEvent(SelectEvent.BUYERSELECT));
}
}
}
Now that when I'm clicking the buyer, the MouseEvent.CLICK is activated and then in the buyer.as, buyercode is traced on the screen. But the event is not dispatched or else it is dispatched but the eventlistener is not executing the code. I'm not getting any runtime errors. the function "showselectbuyer" is never even launched.
How should I solve it?
The accepted answer is incorrect. The provided solution works but for the wrong reasons. It creates a direct dependency to the object supposed to listen to the custom event and then makes that object be the dispatcher. All together this makes the whole idea of using event/custom event unnecessary at best since in that case a simple callback would work just as well. Instead using the useCapture flag would make the whole system work as expected:
addEventListener(SelectEvent.BUYERSELECT,showselectbuyer, true);
The accepted solution is also the inverse way of dealing with non DisplayObject event propagation. The dispatcher should be the listener (no dependencies) and not the listener should be the dispatcher (dependency necessary).
The trouble with custom events is that they don't bubble up the display list, so in case your event to be registered you need to dispatch it to the class instance that has a listener to that event attached. In your case it's an instance of FashionFrenzy. Apparently buyers don't know about a fashion frenzy instance they are running in, so they dispatch the event to themselves and wonder why no one else listens. In order to resolve this, either attach the listener to the buyer, or in the buyer class dispatch the event to parent, which is apparently the instance you want to receive the event.

how to work with event in Flex?

import flash.events.Event;
public class RequestEvent extends Event
{
public static const REQUEST:String = "request";
private var Results:Boolean;
public function get Results():Boolean
{
return _Results;
}
public function RequestEvent(Results:Boolean=false)
{
super(REQUEST);
Results = Results;
}
override public function clone():Event
{
return new RequestEvent(Results);
}
}
}
hi can some body explain why we are doing overridding of function clone and calling super(request), new in flex ........so don't mind.
One needs to implement the clone method just so that Flex could re-clone the event in the case when an event handler wishes to dispatch the same event again. Flex does provide a default implementation but one may override the method to clone the event differently, if need be.
As for calling the super method, you must call the super becasue you are extending the Event class. The type (in your case, REQUEST) must be a unique string that would uniquely identify the event to Flex platform.
Hope it helps
Regards.
The question about the overriding of the clone method in custom events is very popular and it seems to be one of the Flex strange things. You can read about it here.
So you have to override this method and only in this method you can define values of custom properties. The method is not usual, so if you try to debug it you will never get the debugger in its body.
If you try to define the value of your custom property in the constructor, the value will be ignorred.
It can be unpractical to use a constant string as the event's type value. In this case all instances of your RequestEvent are of the same type and you could not tell them appart using in different situations. As you can see in the example below, this string is used in action listener to map the listener function.
In this example I have three buttons with different events - normal Event, my version of RequestEvent and your version of it. Have a look at it, I hope it can help to understand the case.
//Application
<fx:Script>
<![CDATA[
import fld02.com.customevent.RequestEvent;
import fld02.com.customevent.RequestEvent2;
import mx.controls.Alert;
private function onCustomGroupBtn2Clicked(evt:RequestEvent):void
{
Alert.show('Btn2Clicked: results = ' + evt.results.toString(), 'This is RequestEvent');
}
private function onCustomGroupBtn3Clicked(evt:RequestEvent2):void
{
Alert.show('Btn3Clicked: Results = ' + evt.Results.toString(), 'This is your RequestEvent');
}
]]>
</fx:Script>
<customevent:CustomGroup
BUTTON1_CLICKED="{Alert.show('Btn1Clicked', 'This is Event')}"
BUTTON2_CLICKED="onCustomGroupBtn2Clicked(event)"
request="onCustomGroupBtn3Clicked(event)"/>
</s:Application>
//CustomGroup
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="346" height="144">
<fx:Metadata>
[Event(name="BUTTON1_CLICKED", type="flash.events.Event")]
[Event(name="BUTTON2_CLICKED", type="fld02.com.customevent.RequestEvent")]
[Event(name="request", type="fld02.com.customevent.RequestEvent2")]
</fx:Metadata>
<fx:Script>
<![CDATA[
private function onBtn1Click():void
{
this.dispatchEvent(new Event("BUTTON1_CLICKED"));
}
private function onBtn2Click():void
{
var requestEvent:RequestEvent = new RequestEvent("BUTTON2_CLICKED");
requestEvent.results = true;
this.dispatchEvent(requestEvent);
}
]]>
</fx:Script>
<s:Button x="43" y="31" width="183" label="Generate Event" click="onBtn1Click()"/>
<s:Button x="43" y="62" width="183" label="Generate RequestEvent" click="onBtn2Click()"/>
<s:Button x="43" y="93" width="183" label="Generate Your RequestEvent" click="{this.dispatchEvent(new RequestEvent2(true))}"/>
</s:Group>
//My RequestEvent
package fld02.com.customevent
{
import flash.events.Event;
public class RequestEvent extends Event
{
private var _results:Boolean;
public function RequestEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
override public function clone():Event
{
var requestEvent:RequestEvent = new RequestEvent(this.type);
requestEvent.results = this.results;
return requestEvent;
}
public function get results():Boolean
{
return _results;
}
public function set results(value:Boolean):void
{
_results = value;
}
}
}
//Your RequestEvent
package fld02.com.customevent
{
import flash.events.Event;
public class RequestEvent2 extends Event
{
public static const REQUEST:String = "request";
public function RequestEvent2(Results:Boolean=false)
{
super(REQUEST);
Results = Results;
}
private var _Results:Boolean;
public function get Results():Boolean
{
return _Results;
}
override public function clone():Event
{
return new RequestEvent2(Results);
}
}
}

Initialize Assets after Preload

Whenever I export the .swf file of my Flash game, I am receiving "TypeError: Error #1009: Cannot access a property or method of a null object reference.", along with a Runtime Shared Library Preloading Warning for my preloader. I have my timeline organized so that the first and third frames are both empty along with a stop(); command in the Actions layer. The second frame contains a single MovieClip that contains all of my exported assets, which are going to be initialized in the third frame of the timeline. None of my assets, except for the preloader, are exported in the first frame. What changes should I make to my Document Class for it to initialize the assets in the third frame?
Document Class:
package com.gameEngine.documentClass
{
import flash.events.*;
import flash.display.*;
import flash.geom.Point;
import com.gameEngine.assetHolders.*;
import com.gameEngine.assetHolders.Levels.*;
public class Document extends MovieClip
{
private static var _document:Document;
private var preloader:Preloader;
public var mcMain:Player;
public var restartButton:RestartButton;
public var spawnArea:SpawnArea;
public var level_1:Level_1;
public var level_2:Level_2;
public var level_3:Level_3;
public function Document()
{
addEventListener(Event.ADDED_TO_STAGE, init);
_document = this;
preloader = new Preloader(390, this.loaderInfo);
this.addChild(preloader);
preloader.addEventListener("loadComplete", loadAssets);
preloader.addEventListener("preloaderFinished", showLogo);
mcMain = new Player(this);
restartButton = new RestartButton(this);
spawnArea = new SpawnArea();
level_1 = new Level_1(this);
level_2 = new Level_2(this);
level_3 = new Level_3(this);
this.addChild(restartButton);
this.addChild(spawnArea);
this.preloader.x = 400;
this.preloader.y = 250;
restartButton.x = 822.95;
restartButton.y = 19;
spawnArea.x = 400;
spawnArea.y = 250;
trace ("Document Class Initialized");
// constructor code
}
public static function getInstance():Document
{
return _document;
}
private function loadAssets(event:Event):void
{
this.play();
}
private function showLogo(event:Event):void
{
this.removeChild(preloader);
}
public function init(event:Event)
{
if (stage.contains(spawnArea))
{
addChild(mcMain);
}
mcMain.x = spawnArea.x;
mcMain.y = spawnArea.y;
}
}
}
Preloader Class:
package com.gameEngine.assetHolders
{
import com.gameEngine.documentClass.*;
import flash.display.*;
import flash.events.*;
public class Preloader extends MovieClip
{
private var fullWidth:Number;
public var loaderInfo:LoaderInfo;
public function Preloader(fullWidth:Number = 0, loaderInfo:LoaderInfo = null)
{
this.fullWidth = fullWidth;
this.loaderInfo = loaderInfo;
addEventListener(Event.ENTER_FRAME, checkLoad);
}
private function checkLoad (event:Event):void
{
if (loaderInfo.bytesLoaded == loaderInfo.bytesTotal && loaderInfo.bytesTotal != 0)
{
dispatchEvent(new Event("loadComplete"));
phaseOut();
}
updateLoader(loaderInfo.bytesLoaded / loaderInfo.bytesTotal);
}
private function updateLoader(num:Number):void
{
progressBar.width = num * fullWidth;
}
private function phaseOut():void
{
removeEventListener(Event.ENTER_FRAME, checkLoad);
progressBar.gotoAndPlay(2);
if (progressBar.currentFrame == progressBar.totalFrames)
{
phaseComplete();
}
}
private function phaseComplete() : void
{
dispatchEvent(new Event("preloaderFinished"));
}
}
}
You have a lot of race conditions going on here. Many of these events could occur at relatively random times in relation to one another . . . you have to think asynchronously. That is, there can be no assumption that any object exists. E.g., in Document.init(), you check is if the spawnArea exists, but it is almost guaranteed not to at that point, and you never check for it again.
Without making any specific changes, I can recommend a generic solution. For any object (objB) you want loaded after another object (objA) is loaded, have objB created in the objA's ADDED_TO_STAGE handler. A simple example would be:
var objA:Whatever;
var objB:WhateverElse;
[...]
objA = new Whatever();
objA.addEventListener(Event.ADDED_TO_STAGE, objAAddedHnd);
[...]
public function objAAddedHnd(event:Event)
{
// remove the event, if no longer needed:
objA.removeEventListener(Event.ADDED_TO_STAGE, objAAddedHnd);
objB = new WhateverElse();
objB.addEventListener(Event.ADDED_TO_STAGE, objBAddedHnd);
}
[...]
public function objBAddedHnd(event:Event)
{
// remove the event, if no longer needed:
objB.removeEventListener(Event.ADDED_TO_STAGE, objBAddedHnd);
// and so on . . .
}
At this point, it shows that you would need to plan the timeline of object creation.

Flex 4.5 Not listening to custom event dispatched in custom AS class

I have the following situation:
I have an event handler, that displays small messages in my application's statusbar.
These messages get passes through by dispatching events from custom components.
A simple message could be like "HTTP Error" or so.
Now, the main event listener, in the main application file, listens to the event dispatched by any custom component, but seems to refuse listening to events dispatched by custom AS classes.
Here is my code for the custom event:
package main.events
{
import flash.events.Event;
public class ShowNoticeEvent extends Event
{
public var message:String;
public static const SHOW_NOTICE:String = "showNotice";
public function ShowNoticeEvent(type:String, msg:String, bubbles:Boolean = false, cancelable:Boolean = false)
{
super(type, bubbles, cancelable);
this.message = msg;
}
override public function clone():Event
{
return new ShowNoticeEvent(type, message);
}
}
}
This is the event listener in the main application file:
addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
And this is the custom AS class that dispatches the custom event. I pasted all the code, so you could see the whole part of it.
package components.dashboard
{
import components.dashboard.models.*;
/* Event imports */
import flash.events.*;
import main.events.*;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="showNotice", type="main.events.ShowNoticeEvent")]
public class Controller extends UIComponent
{
private var baseUrl:String;
public function Controller(baseUrl:String)
{
this.baseUrl = baseUrl;
}
public function getRunningQuotations():void
{
var runningQuotationsList:RunningQuotationsList = RunningQuotationsList.getInstance();
execService("index.php?a=1", runningQuotationsList.updateList, "pnlRunningQuotations");
}
public function getRecentProjects():void
{
var recentProjectsList:RecentProjectsList = RecentProjectsList.getInstance();
execService("index.php?a=2", recentProjectsList.updateList, "pnlRecentProjects");
}
public function getLatestCustomers():void
{
var latestCustomersList:LatestCustomersList = LatestCustomersList.getInstance();
execService("index.php?a=3", latestCustomersList.updateList, "pnlLatestCustomers");
}
private function execService(url:String, listener:Function, component:String):void
{
var basicService:HTTPService = new HTTPService(baseUrl);
basicService.showBusyCursor = true;
basicService.addEventListener(FaultEvent.FAULT, function(e:FaultEvent):void{httpFault(e, component)});
basicService.method = "POST";
basicService.resultFormat = "text";
basicService.url = url;
basicService.addEventListener(ResultEvent.RESULT, listener);
basicService.send();
}
private function httpFault(event:FaultEvent, component:String = null):void {
var faultstring:String = event.fault.faultString;
var eventObj:ShowNoticeEvent = new ShowNoticeEvent(ShowNoticeEvent.SHOW_NOTICE, faultstring, true);
dispatchEvent(eventObj);
trace(faultstring);
}
}
}
So to sum it all up:
- The event listener listens to the custom event dispatched by any custom component.
- The event listener does not listen to the custom event duspatched by an AS class.
Those who wonder, the event really gets dispatched, that's why I added a trace call.
The instance of Controller Class would have to be added to stage for that to work.
by doing
addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
in the main file you are adding the listener to the stage.
So basically you are doing.
stage.addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true);
If controler instance is not on stage you won't see the event.
You might want to look into a Singleton type pattern for your data management as that would fit this setup pretty good.
Main:
Controller.getLastInstance().addEventListener(ShowNoticeEvent.SHOW_NOTICE, showNoticeListener, true)
.
package components.dashboard
{
import components.dashboard.models.*;
/* Event imports */
import flash.events.*;
import main.events.*;
import mx.controls.Alert;
import mx.core.UIComponent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="showNotice", type="main.events.ShowNoticeEvent")]
public class Controller extends UIComponent
{
static public function getLastInstance():Controller { return _instance; }
static private var _instance:Controller;
private var baseUrl:String;
public function Controller(baseUrl:String)
{
_instance = this;
this.baseUrl = baseUrl;
}
public function getRunningQuotations():void
{
var runningQuotationsList:RunningQuotationsList = RunningQuotationsList.getInstance();
execService("index.php?a=1", runningQuotationsList.updateList, "pnlRunningQuotations");
}
public function getRecentProjects():void
{
var recentProjectsList:RecentProjectsList = RecentProjectsList.getInstance();
execService("index.php?a=2", recentProjectsList.updateList, "pnlRecentProjects");
}
public function getLatestCustomers():void
{
var latestCustomersList:LatestCustomersList = LatestCustomersList.getInstance();
execService("index.php?a=3", latestCustomersList.updateList, "pnlLatestCustomers");
}
private function execService(url:String, listener:Function, component:String):void
{
var basicService:HTTPService = new HTTPService(baseUrl);
basicService.showBusyCursor = true;
basicService.addEventListener(FaultEvent.FAULT, function(e:FaultEvent):void{httpFault(e, component)});
basicService.method = "POST";
basicService.resultFormat = "text";
basicService.url = url;
basicService.addEventListener(ResultEvent.RESULT, listener);
basicService.send();
}
private function httpFault(event:FaultEvent, component:String = null):void {
var faultstring:String = event.fault.faultString;
var eventObj:ShowNoticeEvent = new ShowNoticeEvent(ShowNoticeEvent.SHOW_NOTICE, faultstring, true);
dispatchEvent(eventObj);
trace(faultstring);
}
}
}
Not Ideal since you could only ever have 1 of them.
But I think better than having to turn a simple EventDispatcher into DisplayObject and add it to stage just to Simply bubble.

How do I make my public static logging method write to an inputfield?

I figured out how to create a static method that is available everywhere, for example:
UtilLib.as:
package
{
public final class UtilLib
{
public static function getTimeStamp():uint
{
var now:Date = new Date();
return now.getTime();
}
}
}
I can access this everywhere by doing UtilLib.getTimeStamp() - Now, I want to create a new staic method called log(msg:String). This should log a message to a multi-line inputfield.
The problem however is that this inputfield must be created somewhere and must be accessible and visible all the time, and I don't want to pass it through the function parameters all the time as this would cause a lot of trouble (I'd have to pass it through objects aswell..).
So, how do I make a "public textfield" so my static log method can write to it?
UPDATE:
I now tried the following class, with a static constructor (I think). However, the textfield object is not showing. When I do an addChild(debugField) after creating it, it gives me error 1180.
Logger.as
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldType;
public class Logger extends Sprite
{
public static var debugField:TextField;
/* static block */
{
trace("Logger initializing.");
debugField = new TextField();
debugField.width = 500;
debugField.height = 100;
debugField.x = 100;
debugField.y = 400;
debugField.background = true;
debugField.backgroundColor = 0xFFFFFF;
debugField.defaultTextFormat = new CustomTextFormat();
debugField.mouseWheelEnabled = true;
debugField.multiline = true;
debugField.type = TextFieldType.DYNAMIC;
}
public static function log(msg:String):void
{
if (debugField) debugField.appendText(msg);
}
}
}
I initialize it like this:
var test:Logger = new Logger();
addChild(test);
And I log a new message like this:
Logger.log("test");
Unfortunately, the textField is not showing.
Essentially you need:
somewhere to log a message which is globally accessible
the ability to update a text field whenever the log message changes
A simple solution using objects could look like this:
Example
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.events.Event;
public class Example extends Sprite {
private var messageLog:TextField;
public function Example() {
createTextField();
MessageLogger.getInstance().addEventListener( MessageLogger.LOG, handleMessageLoggerUpdate );
MessageLogger.getInstance().log( "Message!" );
}
private function handleMessageLoggerUpdate( event:Event ):void {
messageLog.text = MessageLogger.getInstance().getLog();
}
private function createTextField():void {
messageLog = new TextField();
addChild( messageLog );
}
}
}
MessageLogger
package {
import flash.events.EventDispatcher;
import flash.events.Event;
public class MessageLogger extends EventDispatcher {
private static var instance:MessageLogger;
public static function getInstance():MessageLogger {
if ( !instance ) {
instance = new MessageLogger( new InstanceKey() );
}
return instance;
}
public static var LOG:String = "MessageLoader#log";
private var messageLog:String;
public function MessageLogger(key:InstanceKey) {
messageLog = "";
}
public function log( message:String ):void {
messageLog += message;
notify();
}
public function getLog():String {
return messageLog;
}
private function notify():void {
dispatchEvent( new Event( LOG ) );
}
}
}
class InstanceKey {}
The important thing here is that a message can be logged from anywhere using
MessageLogger.getInstance().log( "Your Message Here" );
and anything can be notified of when a message has been logged using
MessageLogger.getInstance().addEventListener( MessageLogger.LOG, listenerFunction );
at any point the current message log can be obtained using
MessageLogger.getInstance().getLog();
Create a new Logging class and have that class have a static constructor. Add your logging method to this class. Make the static constructor save the logging field to a private variable. Now before you call the logging method just call your static constructor with the input field you'd like to use. This will create the class, set up the input field as the destination, and now you can simply just call the log function from anywhere.
Traditionally, the way you let static methods interact with private variables is to pass them in. Pass in a pointer to your textbox.
so instead of
public static function getTimeStamp():uint { ... }
you have
public static function writeTimeStamp(messageBox):uint { ... }
The syntax might be incorrect as I'm not an AS dev but, do you see what I mean? From there, that block of code can access messageBox as if it were a local variable. Well it is.
(I renamed the method name for clarity. You could even stop it returning a variable if it doesn't need to but you'll need to change the declaration further.)
In your updated post the debug text field needs to be added to the display list somewhere. Right now it looks like it is just created and used during that log method. You add display objects to the display list by calling addChild( displayObject:DisplayObject ):Boolean; on a DisplayObjectContainer that is already a child of stage.