Strict Typing and the Communication Classes


ActionScript 2.0 introduced new language features like formal class definitions and strict typing that make it easier to build larger and more complex applications. However, many FlashCom developers quickly ran into problems when they tried to use strict typing with the NetConnection, NetStream, SharedObject, and other communication classes.

Let's look at an example using a feature introduced with the Flash Video Exporter 1.1 and updated in the 1.2 release. The newer exporters add stream length and other information at the beginning of each FLV file they export. The information consists of a remote method call named "onMetaData" and an information object that includes properties such as duration, width, height, and framerate.

Using ActionScript 1.0 here's how someone might setup a NetStream in order to trace out the onMetaData information object:

var ns = new NetStream(nc);
ns.onMetaData = function(info) {
for (var p in info){
trace(p + ": " + info[p]);
}
};
ns.play("myStreamName");


Fortunately, this code works fine in both Flash MX and Flash MX 2004. The code does three things:
- creates a new NetStream object and assigns it to the ns variable;
- dynamically adds a method named onMetaData to the NetStream object;
- calls the predefined play() method on the NetStream.

However, if you introduce strict typing by declaring the type of the ns variable you will get a compile-time error in the current (7.2) release of Flash. Here's some ActionScript 2.0 code that will cause the error:

var ns:NetStream = new NetStream();
ns.onMetaData = function(info) {
for (var p in info){
trace(p + ": " + info[p]);
}
};
ns.play("myStreamName");


I've bolded the change in code that declares the ns variable is a NetStream object.

Now, here's the compiler error:

**Error** Scene=Scene 1, layer=Layer 1, frame=1:Line 4: There is no
property with the name 'onMetaData'.
ns.onMetaData = function(info) {


The compiler is complaining that the NetStream class does not have an
onMetaData method and therefore you shouldn't be trying to assign one to a NetStream object. A good question to ask is how does the compiler know that onMetaData isn't a method of the NetStream class? After all, a Macromedia application puts onMetaData method calls in an FLV when it exports FLV files!
The answer is that the compiler looks in the "intrinsic" file for the NetStream class to see what properties and methods are defined as part of the NetStream class. On my home PC the NetStream intrinsic file is located here:

C:\Program Files\Macromedia\Flash MX 2004\en\First Run\Classes\NetStream.as

If you open the file you'll see something like this listing of all the properties and methods of the NetStream class:

// ActionScript Standard Library//
// NetStream object

intrinsic class NetStream
{
var bufferLength:Number;
var bufferTime:Number;
var bytesLoaded:Number;
var bytesTotal:Number;
var currentFps:Number;
var liveDelay:Number;
var time:Number;

function NetStream(connection:NetConnection);

function attachAudio(theMicrophone:Microphone):Void;
function attachVideo(theCamera:Camera,snapshotMilliseconds:Number):Void;
function close():Void;
function pause(flag:Boolean):Void;
function play(name:Object, start:Number, len:Number, reset:Object);
function publish(name:Object, type:String):Void;
function receiveAudio(flag:Boolean):Void;
function receiveVideo(flag:Object):Void;
function seek(offset:Number):Void;
function send(handlerName:String):Void;
function setBufferTime(bufferTime:Number);

function onResult(streamId:Number);
function onStatus(info:Object):Void;
}


Note that there is
no line like this in the file:
function onMetaData(info:Object):Void;

So, as far as the compiler is concerned, NetStream objects should not have an onMetaData method and you shouldn't try to add an onMetaData method to one either! So what to do? Here are some good and not-so-good solutions to the problem:

- don't bother declaring the ns variable as a NetStream;
- wait for Macromedia to add the onMetaData method in a future updater or release of Flash;
- add the declaration to the intrinsic file yourself;
- declare the class dynamic in the intrinsic file;
- create a subclass of the NetStream object that does include an onMetaData method;
- use the [] operator instead of the . operator to dynamically add the onMetaData method;
- some other workaround/option I haven't thought of.

Use the [] operator
Before I waste your time reading about all these options let me say up front that the simplest and therefore often the best solution is to use the [] operator this way:

var ns:NetStream = new NetStream();
ns["onMetaData"] = function(info) {
for (var p in info){
trace(p + ": " + info[p]);
}
};
ns.play("myStreamName");


Once again I've bolded the code that has changed from the previous listing. The [] operator lets you bypass typing of an object and dynamically add any property or method to the object you like. Why is this a good solution? Well, strict typing is meant to save you from all sorts of problems. For example mistakes like this one where I've mistyped a method name:

ns.plya("myStreamName");

If I haven't told the compiler that ns must be a NetStream by declaring var ns:NetStream then the compiler has no way of knowing that there is no method named plya. But if I did use strict typing, the compiler will show me my error and I can correct it easily. Over the long run strict typing can save you hours of tedious debugging. You won't have to run and rerun scripts with trace statements looking for all those little typos you made and often miss. In other words declaring a variable's type is good and you should do it. When you absolutely have to dynamically add a property or method to one of Macromedia's communication objects use the [] notation. The rest of the time use the dot operator and let the compiler catch your errors.

Now lets look at the other options.

Don't Declare the Object's Type
You can always go back to what worked before:

var ns = new NetStream(nc);
ns.onMetaData = function(info) {
for (var p in info){
trace(p + ": " + info[p]);
}
};
ns.play("myStreamName");


After all the nice things I've written about type checking this still isn't too bad. You can use type checking everywhere else but just not use it on this particular NetStream object. It's not my preferred solution but it works and its familiar.

Wait for Macromedia to Update the Intrinsic Files
The truth is that the problem I've outlined isn't really Macromedia's fault at all. They just used an already existing feature of NetStream objects in the FLV exporter. NetStreams have always been able to execute method calls that are recorded in FLV files. Macromedia probably should add onMetaData into the NetStream intrinsic file as a convenience but that won't stop the compiler from complaining about other method call names FlashCom developers use all the time. So waiting for Macromedia to update the NetStream.as intrinsic file is a ridiculous suggestion in a second way: you may need to add your own methods to NetStream objects that Macromedia will never add to the NetStream definition. The same is true of the NetConnection, SharedObject and other communication classes.

Add the Declaration to the Intrinsic File Yourself?
Unless you really know what you are doing and have found a bug in an intrinsic file you want to fix immediately I strongly suggest you not edit intrinsic files.

Declare the Class dynamic in the Intrinsic File?
I don't like this idea either. But for arguments sake you could change this line from this:

intrinsic class NetStream
to this:
dynamic intrinsic class NetStream

Adding the keyword dynamic tells the compiler that its OK to add properties and methods to an object of this class dynamically at any time. Not recommended. If you want a communication object to be dynamic just don't declare its type. Leave the intrinsic files alone.

Create a NetStream Subclass
Creating a subclass of some of the communication classes can be a really good idea. You define the methods and properties you need in addition to the ones provided by Macromedia. Returning to the onMetaData method for a moment you could do something as simple as this:

class MetaNetStream extends NetStream{
function MetaNetStream(nc){
super(nc);
}
var onMetaData:Function;
}


You would save the code in a file named MetaNetStream.as. Then you could do this:

var ns:MetaNetStream = new MetaNetStream(nc);
ns.onMetaData = function(info) {
for (var p in info){
trace(p + ": " + info[p]);
}
};
ns.play("myStreamName");


The compiler will not complain and it will work. At least it does for me. The advantage of creating a NetStream subclass like that is that you can declare a few methods and properties that you almost always add dynamically and the compiler will not care. In many cases you should take it further and simply move the definition of the things you want to add to the NetStream subclass. For example:

class MetaNetStream extends NetStream{
function MetaNetStream(nc){
super(nc);
}
function onMetaData = function(info) {
for (var p in info){
trace(p + ": " + info[p]);
}
}
}


Now you can use your code this way:

var ns:MetaNetStream = new MetaNetStream(nc);
ns.play("myStreamName");


...without the mess of adding methods dynamically outside of your class files.

What About NetConnection and SharedObject?
You have the same options with the NetConnection class as with the NetStream class. Just as with the NetStream class, a good option is to subclass the NetConnection class. Shared objects are not so simple. The SharedObject class cannot be subclassed. So you have to either add to the SharedObject.prototype or dynamically customize each shared object after you call SharedObject.getRemote( ) in Flash or SharedObject.get( ) on the server.
Even then it usually is not a good design idea to customize shared objects very much. Its much better to just set them up as event broadcasters and let some other object or component control each one. Of course I'm glossing over a lot of detail because this article is getting long enough. But I write about it all in
Programming Flash Communication Server forthcoming from O'Reilly. I've also included some useful references below.

Summary
Despite my joking around about waiting for Macromedia to update the NetStream.as file the truth is when the compiler in Flash 7.2 complains that "there is no property with the name..." its likely telling you the truth. (Earlier versions of the intrinsic files did have some annoying errors.)

So really you can't have it both ways. If you want strict type checking you can't dynamically add properties and methods that you have not declared and expect the compiler not to complain - at least not with the dot operator. If you want strict type checking and you want to add properties and methods to a NetStream you have to declare that you are going to do that in advance or use the [] operator. If you want to declare other properties and methods in advance create a NetStream subclass. Strict type checking is always more work up front on the theory that it will pay off later.

0 Response to Strict Typing and the Communication Classes

Post a Comment