Section 3.6. Subclassing the NetConnection Class

3.6. Subclassing the NetConnection Class

You can extend the features of the NetConnection class by creating a subclass based on it. Creating subclasses lets you encapsulate code that operates on the NetConnection object within the subclass's methods such as connect( ) , onStatus( ) , or close( ) . In turn , this can lead to more modular and easier to maintain applications. The key to doing this is to use the super operator. Example 3-8 shows a very simple example of how to extend the NetConnection class and then how to use it within a simple test script.

Example 3-8 uses AS 1.0 syntax so it works in both Flash MX and Flash MX 2004. This also allows you to compile using ActionScript 1.0 under File Publish Settings ActionScript. Example 3-10 provides an alternative implementation that uses AS 2.0 syntax and the v2 UI component's EventDispatcher class.

Example 3-8. A simple extension of the NetConnection class
 // Constructor function must call   super(  )   . function NetConnectionSubClass ( ) {   super( ); } // Subclass   NetConnection   by assigning an instance of it // to the   prototype   object of the new subclass. NetConnectionSubClass.prototype = new NetConnection( ); // A simple demonstration   onStatus( )   event handler that just // writes out each   info.code   message. NetConnectionSubClass.prototype.onStatus = function (info) {   trace("info.code: " + info.code); }; /* This   connect( )   method may be passed any number of arguments.  * The   apply( )   method makes sure all arguments are passed into  * the   super.connect( )   method when it is called.  */ NetConnectionSubClass.prototype.connect = function ( ) {   return super.connect.apply(super, arguments); }; // Create an instance of the   NetConnectionSubClass   subclass. lobby_nc = new NetConnectionSubClass( ); // Attempt the connection. if (lobby_nc.connect("rtmp:/testLobby/", "Guest", "Guest")) {   trace("Attempting connection..."); } else {   trace("Can't attempt connection. Is the URI correct?"); } 

The super operator is used in two different ways in Example 3-8. First, it must be called within the constructor function as follows :

 super(  ); 

Invoking super( ) in this way calls the NetConnection constructor (the superclass's constructor). Second, whenever a NetConnection method must be called by a subclass's method, the super operator can be used to call the superclass's method. In this example, when the NetConnectionSubClass.connect( ) method is called, it in turn uses super to call the NetConnection.connect( ) method as follows:

 return super.connect.apply(super, arguments); 

Using the apply( ) method provides a good, general purpose way to call the superclass's connect( ) method. Passing super as a parameter ensures that connect( ) is called as a method of the NetConnection superclass and passing arguments insures that all the parameters passed to the subclass's connect( ) method are passed on to the superclass's connect( ) method.

A simpler, but less general purpose, way to call the NetConnection.connect( ) method is to specifically pass individual parameters this way:

 NetConnectionSubClass.prototype.connect = function (targetURI, userName, password) {   return super.connect(targetURI, userName, password); }; 

In cases in which parameter values must be manipulated before being passed to the superclass's connect( ) method or those in which not all parameters must be passed on, it is often simpler to avoid using the apply( ) method.

On its own, Example 3-8 shows the basic mechanics of creating a subclass of the NetConnection class, but let's see what advantages this offers. Looking back at Example 3-4 and Example 3-6, you will see that before calling the connect( ) or close( ) methods, the handleCloseEvents flag had to be set each time. This is extra work that shouldn't have to be done in the doConnect( ) , doChat( ) , and doLobby( ) functions.

In fact, these details can be hidden inside a NetConnection subclass's methods as follows:

 //   close(  )   turns off the   handleCloseEvents   flag before closing the connection. LobbyChatConnection.prototype.close = function (  ) {   this.handleCloseEvents = false;   super.close( ); }; 

If we modify the connect( ) method to set the handleCloseEvents flag as well, the doLobby( ) function can be greatly simplified:

 // Called when the Lobby button in a chat room is clicked. function doLobby(btn) {   lobbyChat_nc.close(  );   lobbyChat_nc.connect("rtmp:/testLobby", handleLobbyConnection,                         userName, password); } 

Compare the preceding code to the doLobby( ) function in Example 3-6. You may notice that something else is missing. In the "Reusing a NetConnection Object" section of this chapter, two onStatus( ) event handlers were written: one for managing lobby connections and the other for managing chat room connections. The doLobby( ) and doChat( ) functions were responsible for making sure the right onStatus( ) handler was in place before calling connect( ) . In some cases, this is necessary. However, when the onStatus( ) handlers are almost identical, there is a more elegant solution. The main difference between the handlers is the frame to which they move the playhead when a connection is made. A more general purpose onStatus( ) handler can call a function when a connection is established. In Example 3-9, one of two simple functions is called by the onStatus( ) handler depending on what application it connects to:

 // Called when a connection has been established to the   testLobby   app. function handleLobbyConnection(  ) {   writeln("Success, you are connected to the lobby!");   gotoAndPlay("Lobby"); } // Called when a connection has been established to a chat room. function handleChatConnection( ) {   writeln("Success, you are connected to a chat room!");   gotoAndPlay("ChatRoom"); } 

How does the onStatus( ) handler know which of these functions to call? In this example, a reference to one of them is passed into the connect( ) method and saved in the connectionHandler property. Example 3-9 is a complete listing of a test script in which the onStatus( ) method uses the connectionHandler property to call either the handleLobbyConnection( ) or handleChatConnection( ) function.

Example 3-9. NetConnection subclass test script
 //   writeln( )   writes messages into a text field named   trace_txt   // and is a replacement for using   trace( )   and the Output panel. function writeln (msg) {   trace_txt.text += msg + "\n";   trace_txt.scroll = trace_txt.maxscroll; } //   LobbyChatConnection( )   is a constructor function for a   NetConnection   subclass. // It will not work unless   super( )   is called within the function. function LobbyChatConnection ( ) {   super( ); } LobbyChatConnection.prototype = new NetConnection( );     // Subclass   NetConnection   . LobbyChatConnection.prototype.handleCloseEvents = true;  // Initial value. //   connect( )   is always passed four parameters, updates the   handleCloseEvents   flag, // and calls the superclass's   connect( )   method. LobbyChatConnection.prototype.connect = function (targetURI, connectionHandler,                                                   userName, password) {   this.connectionHandler = connectionHandler;   this.handleCloseEvents = true;   var result = super.connect(targetURI, userName, password);   if (result) {     writeln("Please wait. Attempting connection...");   }   else {     writeln("Can't attempt connection to: " + this.uri);     writeln("Is the URI correct?");   }   return result; }; //   close( )   turns off the   handleCloseEvents   flag before closing the connection. LobbyChatConnection.prototype.close = function ( ) {   this.handleCloseEvents = false;   super.close( ); }; /*   onStatus( )   calls the current   connectionHandler   when a successful connection  * is made. It reports closed connections and errors except when closed  * connections are expected. When a connection is closed, the playhead is  * sent to the   Login   frame.  */ LobbyChatConnection.prototype.onStatus = function (info) {   // Always deal with successful connections.   if (info.code == "NetConnection.Connect.Success") {     this.handleCloseEvents = true;     this.connectionHandler( );   }   // Handle messages when the connection is closed.   if (!this.isConnected && this.handleCloseEvents) {     // Always handle rejection messages when they occur.     if (info.code == "NetConnection.Connect.Rejected") {       writeln(info.application.Message);       writeln('Did you use the username "Guest" and password "Guest" ?');     }     else {       writeln("Error: Connection Closed.");     }     this.handleCloseEvents = false;     gotoAndPlay("Login");   }   // Handle remote method call errors here if you need to. }; // Called when a connection has been established to the   testLobby   app. function handleLobbyConnection ( ) {   writeln("Success, you are connected to the lobby!");   gotoAndPlay("Lobby"); } // Called when a connection has been established to a chat room. function handleChatConnection ( ) {   writeln("Success, you are connected to a chat room!");   gotoAndPlay("ChatRoom"); } // Second, create a   LobbyChatConnection   object. lobbyChat_nc = new LobbyChatConnection( ); // When the Connect     button in the   Login   frames is clicked, this // function attempts to connect to the   testLobby   application. function doConnect ( ) {   // Keep track of the username and password   _global.userName = userName_txt.text;   _global.password = password_txt.text;   // Try to connect to the lobby.   lobbyChat_nc.connect("rtmp:/testLobby", handleLobbyConnection,                        userName, password); } // Called when the Lobby button in a chat room is clicked. function doLobby (btn) {   lobbyChat_nc.close( );   lobbyChat_nc.connect("rtmp:/testLobby", handleLobbyConnection,                        userName, password); } // Called when the Chat button in the lobby is clicked. function doChat (btn) {   lobbyChat_nc.close( );   lobbyChat_nc.connect("rtmp:/testChat/room1", handleChatConnection,                        userName, password); } // Called when the Disconnect button is clicked. function doDisconnect ( ) {   lobbyChat_nc.close( );   gotoAndPlay("Login"); } gotoAndPlay("Login"); 

The onStatus( ) handler in Example 3-9 moves the playhead to the Login frame itself. If necessary, the more general purpose mechanism of calling a function could be used in the same way that the connectionHandler( ) function was called.

Now let's look at how to create a NetConnection subclass using ActionScript 2.0. The FCSConnector class defined in Example 3-10 is just one way to do it. Like the previous examples, it uses an internal handleCloseEvents property to remember whether it should report a "NetConnection.Connect.Closed" code. It is also designed to take advantage of the event-passing system introduced in the UI components distributed with Flash MX 2004. Whenever onStatus( ) is called, one of several possible events is dispatched to any objects that have added themselves as event listeners. Events are dispatched by the dispatchEvent( ) method. You may notice in the listing that the dispatchEvent( ) method is never defined. In fact, it is added to each FCSConnector object when the EventDispatcher class initializes the object in the FCSConnector( ) function.

Example 3-10. An ActionScript 2.0 NetConnection subclass
 class com.oreilly.pfcs.FCSConnector extends NetConnection {   // EventDispatcher needs these.   var addEventListener:Function;   var removeEventListener:Function;   var dispatchEvent:Function;   var dispatchQueue:Function;   // Internal data.   var handleCloseEvents:Boolean = true;   function FCSConnector ( ) {     super( );     mx.events.EventDispatcher.initialize(this);   }   function connectionClosed (type, info) {     if (handleCloseEvents) dispatchEvent({target:this, type:type, info:info});     handleCloseEvents = false;   }   function onStatus (info) {     switch (info.code) {       case "NetConnection.Connect.Success":         dispatchEvent({target:this, type:"onConnect", info:info});         break;       case "NetConnection.Connect.Rejected":         connectionClosed("onReject", info);         break;       case "NetConnection.Connect.Closed":         connectionClosed("onClose", info);         break;       case "NetConnection.Connect.Failed":         connectionClosed("onFail", info);         break;       case "NetConnection.Connect.AppShutdown":         connectionClosed("onClose", info);         break;       case "NetConnection.Connect.InvalidApp":         connectionClosed("onReject", info);         break;       case "NetConnection.Call.Failed":         dispatchEvent({target:this, type:"onCall", info:info});         break;     }   }   function connect ( ) {     handleCloseEvents = true;     return super.connect.apply(super, arguments);   }   function close ( ) {     handleCloseEvents = false;     super.close( );   } } 

The FCSConnector class does not simply create an onStatus event and pass on the information object as part of the event. Instead its onStatus( ) method looks at the info.code string it receives and dispatches one of the following events: onConnect , onReject , onFail , onClose , or onCall . Creating separate events has advantages and disadvantages. It allows someone using the FCSConnector class to receive only the events he is interested in and to write separate functions to handle each type of event. However, in some applications, it may be less work to handle a single onStatus event. Example 3-11 lists code for a simple demonstration movie that uses the FCSConnector class. It imports the FCSConnector class, stores a single instance of it in the nc variable, and then adds the main timeline as a listener for all four connection- related events. Each event is delivered to a function or method of the same name .

Example 3-11. Importing and using the FCSConnector class in the main timeline of a movie
 import com.oreilly.pfcs.FCSConnector; // Create a new FCSConnector. nc = new FCSConnector( ); // Add the   _root   timeline as a listener for connection-related events. // Note: Since this code is on the main timeline   this   refers to   _root   . nc.addEventListener("onReject", this); nc.addEventListener("onFail", this); nc.addEventListener("onClose", this); nc.addEventListener("onConnect", this); // Handle "NetConnection.Connect.Failed" messages here. function onFail (ev) {   var info = ev.info;   writeln("Can't reach the server.")   writlen("Please check your network connection and try again.");   writeln("info.code: " + info.code);   connectButton.label = "Connect"; } // Handle "NetConnection.Connect.Success" messages here. function onConnect (ev) {   var info = ev.info;   writeln("You are now connected.");   writeln("info.code: " + info.code);   connectButton.label = "Disconnect"; } // Handle "NetConnection.Connect.Rejected" messages here. function onReject (ev) {   var info = ev.info;   writeln("Your connection attempt was rejected.");   writeln("info.code: " + info.code);   writeln("info.description: " + info.description);   if (info.application) {     for (var p in info.application) {       writeln("info.application." + p + ": " + info.application[p]);     }   }   connectButton.label = "Connect"; } // Handle "NetConnection.Connect.Closed" messages here. function onClose (ev) {   var info = ev.info;   writeln("Your connection was closed.");   writeln("info.code: " + info.code);   connectButton.label = "Connect"; } connectButton.addEventListener("click", this); function click (ev) {   var button = ev.target;   if (button.label == "Connect") {     if (nc.connect("rtmp:/testLobby",                    userNameTextInput.text,                    passwordTextInput.text)) {       button.label = "Wait...";       writeln("Attempting to connect...");     }     else {       writeln("Can't attempt connection. Check the URI.");     }   }   else {     writeln("Connection closed.");     button.label = "Connect";     nc.close( );   } } function writeln (msg) {   msgTextArea.text += msg + "\n";   msgTextArea.vPosition = msgTextArea.maxVPosition;   msgTextArea.redraw( );// Fixes scrolling bug in TextArea as of Flash 7.2. } 

All the preceding examples are available in a Zip archive file from http://flash-communications.net. The archive includes the file com/oreilly/pfcs/FCSConnector.as . That file must be extracted from the archive with its path intact (most unZip programs have a Maintain Folders option for this purpose) and placed in a directory in the classpath of Flash MX 2004. See Chapter 9 of Essential ActionScript 2.0 (O'Reilly) for more information on packages and the classpath.



Programming Flash Communication Server
Programming Flash Communication Server
ISBN: 0596005040
EAN: 2147483647
Year: 2003
Pages: 203

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net