AS3: Tracing in Flash/Flex
Inspired by the article of Dru Kepple I decided to share my experience in Debugging processes in flash/flex enviroment.
The article of Dru Kepple explains very well the types of errors related to the AS3 development. I'm not going to digg in this area, but really recommend to check this out.You can download all the files used in this article here.
1. Simple tracing
I'm absolutelly sure that you know the trace method. It gives us ability to show some data in the Output panel. It is extremely helpful when you have to see the value of some variable or just to follow the workflow of you application.
trace("This text will be shown in the Output panel");
It's good to know that, together with the Output panel the string used in trace method is also sent to a file located on your computer. Depending on your operating system the file is stored in different locations:
Windows 95/98/ME/2000/XP:C:\\Documents and Settings\\username\\Application Data\\Macromedia\\Flash Player\\LogsWindows Vista:C:\\Users\\username\\AppData\\Roaming\\Macromedia\\Flash Player\\LogsMacintosh OS X:/Users/username/Library/Preferences/Macromedia/Flash Player/Logs/Linux:/home/username/.macromedia/Flash_Player/Logs/
When you run your flash app in a browser you can check this file and you will probably see your traces. More information about the logging process of the flash player could be found on this page.
2. Tracing by using a wrapper class
The trace function is good, but I prefer to use a wrapper for it. By doing that I can disable/enable the logging whenever I want. The class also stores the whole logging text and it could be saved or sent somewhere.
package {
public class Debug {
public static
var enable: Boolean = true;
private static
var _text: String = "";
public static
function echo(str: Object): void {
if (enable) {
trace("-> " + str);
_text = "-> " + str + "\\n" + _text;
}
}
}
}
If you are using Adobe Flash as a development enviroment you have probably seen an option in the Publish Settings panel called Omit trace actions. If you select it, your traces will not be sent to the Output panel or stored in the log file. I'm using FlashDevelop together with Flex SDK and there is an option to build the project in Release mode. This also prevents the showing of the traces. Because of such kind of cases or because I need to see the traces directly in the browser I added additional functionalities to the Debug class. I created a debug holder - MovieClip with TextField inside. As you may guess the logging text is shown in the TextField. The holder is hidden by default. It's visible only when the user clicks several special keys. I decided to use the following combination - Ctrl + Shift + F11. What you have to do is to call the addDebugHolder method once in your application passing a DisplayObject. Here is the full source code of the Debug class:
package {
import flash.display.MovieClip;
import flash.events.KeyboardEvent;
import flash.net.*;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.*;
public class Debug {
public static
var enable: Boolean = true;
private static
var _textFieldAsOutput: TextField;
private static
var _text: String = "";
private static
var _debugHolder: MovieClip;
private static
var _root: * ;
public static
function echo(str: Object): void {
if (enable) {
_text = "-> " + str + "\\n" + _text;
trace("-> " + str);
if (_debugHolder) {
updateDebugHolder();
}
}
}
public static
function addDebugHolder(rootMovie: MovieClip, txtWidth: Number = 400, bgAlpha: Number = 0.9, autoShow: Boolean = false, fontColor: uint = 0x999999): void {
_root = rootMovie;
_debugHolder = new MovieClip();
_debugHolder.visible = autoShow;
var bg: MovieClip = new MovieClip();
bg.name = "background";
bg.graphics.beginFill(0xFFFFFF, bgAlpha);
bg.graphics.drawRect(0, 0, txtWidth, 1);
var tf: TextFormat = new TextFormat();
_textFieldAsOutput = new TextField();
tf.font = "Verdana";
tf.size = 9;
tf.color = fontColor;
_textFieldAsOutput.autoSize = TextFieldAutoSize.LEFT;
_textFieldAsOutput.defaultTextFormat = tf;
_textFieldAsOutput.width = txtWidth;
_debugHolder.addChild(bg);
_debugHolder.addChild(_textFieldAsOutput);
rootMovie.addChild(_debugHolder);
rootMovie.stage.addEventListener(KeyboardEvent.KEY_DOWN, onRootKeyDown);
}
private static
function onRootKeyDown(e: KeyboardEvent): void {
if (e.ctrlKey && e.shiftKey && e.keyCode == 122) {
if (_debugHolder) {
if (_debugHolder.visible) {
_debugHolder.visible = false;
} else {
_debugHolder.visible = true;
}
}
}
}
private static
function updateDebugHolder(): void { // setting text _textFieldAsOutput.text = _text; // updating background's height _debugHolder.getChildByName("background").height = _textFieldAsOutput.height; // updating holder's display list position (it should be always on top) if(_root.getChildAt(_root.numChildren-1) != _debugHolder) { if(_root.contains(_debugHolder)) { _root.removeChild(_debugHolder); } _root.addChildAt(_debugHolder, _root.numChildren-1); } } }}
Simple example that illustrates the usage of the class:
var swf = new FlashObject( "http://krasimirtsonev.com/files/debugging/UsingDebugClass.swf", "animationSwf", "550", "300", "9", "#000000" ); window.onload = function() { swf.write("animation"); }; Every click on the big gray area adds a new rectangle to the stage filled with the color selected in the ColorPicker. Make some clicks and hit Ctrl + Shift + F11.
3. SWF to SWF communication
Another interesting way of debugging is to make a connection between your SWF(let's call it client) and some other SWF(let's call it host). The idea is to send and monitor your traces in the host application. For example firstly, open this url (it contains the host SWF) and then the client here. Click several times in the client's SWF and see the result in the other side. What I used for this communication is LocalConnection class. Here is the code of the two SWFs:
// the host's swf
package {
import flash.display.MovieClip;
import flash.net.LocalConnection;
public class UsingSWF2SWFHost extends MovieClip {
private
var _conn: LocalConnection;
private
var _debugStr: String = "";
public
function UsingSWF2SWFHost() {
debug("constructor");
_conn = new LocalConnection();
_conn.allowDomain("*");
_conn.client = this;
try {
_conn.connect("SWF2SWFDebugging");
} catch (err: Error) {
debug("Can't connect!");
}
}
private
function debug(str: String): void {
field.text = _debugStr = str + "\\n" + _debugStr;
}
public
function callDebug(str: String): void {
debug(str);
}
}
}
// the client's swf
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.LocalConnection;
import flash.events.StatusEvent;
public class UsingSWF2SWFClient extends MovieClip {
private
var _conn: LocalConnection;
public
function UsingSWF2SWFClient(): void {
_conn = new LocalConnection();
_conn.addEventListener(StatusEvent.STATUS, onStatus);
debug("constructor");
stage.addEventListener(MouseEvent.CLICK, onStageClick);
}
private
function onStageClick(e: Event): void {
debug("onStageClick x=" + mouseX + " y=" + mouseY);
}
private
function debug(str: String): void {
trace(str);
try {
_conn.send("SWF2SWFDebugging", "callDebug", str);
} catch (err: Error) {
trace("Can't send! Message: " + err.message);
}
}
private
function onStatus(event: StatusEvent): void {
switch (event.level) {
case "status":
// trace("send() succeeded");
break;
case "error":
trace("send() failed");
break;
}
}
}
}
Have in mind that the maximum data that you can send between the SWFs is 40K. Something else that I have to mention here is that you can send an object. It is passed by value not by reference, but you can still make some useful things like transferring JSON object, accessing public variable or even call a public method from the client's SWF file.
4. Debugging with Monster Debugger
Monster Debugger is probably the most useful debugger tool that I found. It is an open source debugger for Adobe Flash, Flex and AIR. I believe that the authors of this application use similar SWF to SWF communication with LocalConnection. I strongly recommend to check their tour page and give a chance to the debugger. It will definitelly save you a lot of time.You can download all the files used in this article here.