Flex applications are capable of many types of data communication, from the simple to the complex. Often when we think of data communication in Flex applications we think of client/server communications such as remote procedure calls (RPCs). However, some types of data communication occur entirely on the client side, and these types are the subject of this chapter.
At a minimum, all Flex applications require a client-side element in the form of a .swf file running in Flash Player. Some Flex applications even use several .swf files running in one or more instances of Flash Player on the client machine. The client-side portion of a Flex application is capable of several types of client data communication intended for a variety of purposes.
There are three main ways in which a Flex application can run data communications on the client:
A local connection allows two .swf files to communicate as long as they are running on the same client machine at the same time. The .swf files can be running in two different instances of Flash Player. They can even be running in different host environments. For example, one .swf can be running in a web browser while another is running embedded within an executable running on the desktop. The .swf files can even communicate while served from different domains if configured correctly.
Local shared objects allow the application to store and retrieve persistent data on the client machine. For example, a user can save preferences that the application can retrieve automatically the next time the application runs.
The external interface is a mechanism by which a Flex application can communicate with the host environment. This allows the Flex application to run as an integrated part of a larger application. In practical terms, this means communicating with JavaScript in the containing HTML page.
The Flex framework does not provide special behaviors for working with client data communications. Rather, all of the topics in this book utilize low-level Flash Player API ActionScript.
Local connections are a way in which two .swf files can communicate even if they are running in two different Flash Player instances. Local connections allow you to create integrated applications composed of two or more .swf files running in separate Flash Player instances, such as several pods or modules that are part of a complex rich Internet application (RIA) that uses both Flex elements and HTML elements. Furthermore, local connections allow you to communicate between Flash 9 content (Flex applications) and older, legacy Flash content (Flash 8 or earlier).
Local connections use Action Messaging Format (AMF), a binary messaging format, as the protocol for local
connection data packets. A local connection request uses one AMF packet,
and the maximum size for a local connection AMF packet is 40 KB. Local
connections exclusively use a form of AMF called AMF0. This is in
contrast with other ActionScript classes (such as flash.net.NetConnection
), which can use AMF0
as well as AMF3, a newer form of AMF. AMF0 is compatible with older
Flash content, making local connections a compatible way to communicate
from Flex applications to older Flash content.
Typically when implementing local connections, at least two .swf files are needed: one that sends the requests and one that receives the requests. You cannot establish a local connection with a .swf without its explicit consent, because the receiving .swf must have the necessary code to listen for the specific requests.
Technically it is possible to use a local connection to send and receive requests all within one .swf. However, in all practical cases, you will use two .swf files.
Both the sending and receiving .swf files use the flash.net.LocalConnection
object. The LocalConnection
object communicates over a named connection channel. The name of
the channel is arbitrary and is a string value, but for the
communication to work, the sending and receiving .swf files must send and receive over a
channel with the same name.
The sending .swf uses the
send()
method of a LocalConnection
object to send the request.
The send()
method requires at least
two parameters specifying the name of the channel and the name of the
method to call on the receiving .swf. The following example creates a new
LocalConnection
instance and calls
the send()
method:
var localConnection:LocalConnection = new LocalConnection(); localConnection.send("channel", "exampleMethod");
If the method in the receiving .swf expects parameters, you can pass them to
the send()
method following the name
of the method to call. For instance, the following example calls
exampleMethod
on the receiving
.swf and passes it integer
parameters:
localConnection.send("channel", "exampleMethod", 10, 25);
The receiving .swf must
listen for requests on the same channel as the sending .swf sends them. You can instruct a LocalConnection
object to listen for requests
by calling the connect()
method,
passing it the name of the channel to which to listen:
var receivingLocalConnection:LocalConnection = new LocalConnection(); receivingLocalConnection.connect("channel");
If you do not call the connect()
method, the .swf will have no way of knowing that it
should be listening for requests. In such a case, a sending .swf will throw errors if it cannot find
the connection over which to send requests.
You must also tell the LocalConnection
object where to direct the
requests. For instance, when the sending .swf makes a request for exampleMethod
, the receiving LocalConnection
object needs to know
where it can find exampleMethod
. You
can tell it where to find the method by assigning a reference to the
appropriate object to the client property of the LocalConnection
object. For example, the
following tells the LocalConnection
object on the receiver application where it can find the requested
methods as methods of this class:
receivingLocalConnection.client = this;
The methods that you expose via a local connection must be
declared as public
. Otherwise, the
application will throw an error. Methods must be defined for the
object specified as the client of the receiving LocalConnection
object.
This step of setting the client is essential. Without setting the
client
property, the LocalConnection
object will throw errors when
it receives requests.
Example 16-1 requires two MXML
files compiled into two .swf files.
The first MXML file creates the sending .swf. It contains a text area and a button.
When the user clicks the button, the event handler sends a local
connection request to call a method named displayMessage
with a parameter equal to the
value of the text area text.
Example 16-1. Local connection send example
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import flash.net.LocalConnection; private var _localConnection:LocalConnection = new LocalConnection(); private function sendMessage(event:MouseEvent):void { _localConnection.send("dataChannel", "displayMessage", message.text); } ]]> </mx:Script> <mx:VBox id="vbox"> <mx:TextArea id="message" /> <mx:Button label="Send" click="sendMessage(event)" /> </mx:VBox> </mx:Application>
For the preceding example to clearly work, you must enter text into the text area before clicking the Send button.
The second MXML document defines the receiving .swf. This file (Example 16-2) contains a text area.
Example 16-2. Local connection receive example
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="initializeHandler(event)"> <mx:Script> <![CDATA[ import flash.net.LocalConnection; private var _localConnection:LocalConnection; private function initializeHandler(event:Event):void { _localConnection = new LocalConnection(); _localConnection.connect("dataChannel"); _localConnection.client = this; } // Note that this method is declared as public because it is // exposed as a method to a local connection. public function displayMessage(message:String):void { output.text += message + " "; } ]]> </mx:Script> <mx:TextArea id="output" width="539" height="589"/> </mx:Application>
The initializeHandler()
method
runs when the application initializes because it is set to handle the
application initialize event, and it creates the LocalConnection
object, connects to the
channel to listen for requests, and designates this as the client for
the requests, meaning that the requests get routed to methods of the
same name defined for the MXML document. Notice that displayMessage()
is declared as public
. Methods called via a local connection
must be declared as public
.
One nonobvious use for local connections is to allow inter-.swf communication between Flash 9 (Flex) applications and content published to Flash 8 or earlier. Flex applications can load any sort of .swf, whether it was published from Flex or any version of Flash authoring. However, because Flash 9 applications use a fundamentally different virtual machine than older Flash content, it’s not possible for a Flash 9 application to communicate directly with a .swf published from Flash 8 or earlier. If a Flash 9 application loads a Flash 9 .swf, they can communicate directly by calling methods on the loaded .swf. However, that’s not possible when a Flash 9 application loads a Flash 8 or earlier .swf. That’s because Flash 9 and later content uses a completely separate ActionScript virtual machine (AVM) from that used by older content.
Local connection communication is a solution for interoperability, as it is supported by both older Flash content as well as Flash 9 applications. You can create a local connection API in the legacy content that the Flex application can call once it loads the .swf.
In our discussion of local connection communication, we have thus far assumed that all communicating .swf files are in the same domain. By default, Flash Player disallows local connection communication when the .swf files are being loaded from different domains. However, you can explicitly allow cross-domain communication for a specific receiving .swf.
There are two basic types of cross-domain local connection communication: known domains communication and unknown domains communication. Known domains communication occurs when both the sending and receiving applications know about one another and the domains from which they are hosted. However, there are many cases in which the domains are not necessarily known at compile time. For example, you may use a local connection to create a plug-in-style application that can interact with many different applications. In such a case, you don’t necessarily know what all the possible domains are ahead of time.
The technique for unknown domains will work for both unknown and known domains, but it is also more lax. Therefore, it is recommended that you use the known domains technique whenever possible. If you must enable cross-domain communication but you do not know the domains from which the .swf files will be hosted, you can use the unknown domains technique.
To allow cross-domain local connection communication for known
domains you must do two things: explicitly tell the receiving LocalConnection
object to allow requests from
the sending domain, and prefix the sending request channel name with the
domain of the receiving .swf. Use
the allowDomain()
method to specify a list of all the domains to allow.
For the next example assume that the sending .swf is hosted at www.a.com and the receiving .swf is hosted at www.b.com. The following illustrates the code to send a request:
var localConnection:LocalConnection = new LocalConnection();
localConnection.send("www.b.com:
channel", "exampleMethod");
The following illustrates the code necessary to receive the request with an application hosted at www.a.com:
var receivingLocalConnection:LocalConnection = new LocalConnection();
receivingLocalConnection.allowDomain("www.a.com");
receivingLocalConnection.connect("channel");
receivingLocalConnection.client = this;
When the domains are unknown, you can use the wildcard character
(*
) when calling allowDomain()
, and rather than prefixing the
channel with the receiving domain, you can name the channel with an
initial underscore:
// Sending .swf var localConnection:LocalConnection = new LocalConnection(); localConnection.send("_channel
", "exampleMethod"); // Receiving .swf var receivingLocalConnection:LocalConnection = new LocalConnection(); receivingLocalConnection.allowDomain("*
"); receivingLocalConnection.connect("channel"); receivingLocalConnection.client = this;
When testing cross-domain communication, it's useful to run the application using the debugger, which will notify you if there is a problem.