Monday, October 27, 2008

Communication Between Two Flex Applications

More than one attendee at the Colorado Software Symposium 2008 who attended my presentation Apply Flash to Java: Flex and OpenLaszlo asked how to allow two different Flash applications (.swf) files in the same web browser communicate with one another. Because of the obvious interest in this, I threw together a brief and simple example that will be included on the CD that is distributed to conference attendees. I thought this might interest others as well, so I am writing this blog entry to include the example and provide a basic outline of how it works. In an attempt to kill two birds with one stone, I also used this example to illustrate embedding SWF applications within HTML using SWFObject.

The main ActionScript class of interest for the Flex-to-Flex communication used in this blog entry is the LocalConnection class. Two instances of the LocalConnection class are required; one for each of the SWF applications that will be communicating. The most important methods used in LocalConnection in this blog entry's examples are the send() and connect() methods.

The ASDoc documentation for LocalConnection provides highly descriptive detail concerning use of the LocalConnection class. Other good introductory information on use of LocalConnection is available in Connecting to Other Flash Player and AIR Instances.

For this example, let's assume that one SWF application is calling another SWF object with the calling SWF application being the "sender" and the SWF application that is being called being the "receiver." The "receiver" SWF application will need to use LocalConnection's connect(<connectionName>) method while the "sender" SWF application will use the LocalConnection's send(<connectionName>, <methodName>,<zeroToManyMethodArguments>) method. As you'd expect, the <connectionName> passed to the LocalConnection object of the "sender" must match that passed to the "receiver" LocalConnection.connect(<connectionName>) call. Also, the <methodName> specified in the "sender" use of LocalConnection.send() must match a method that exists in the "receiver" object. Finally, zero or multiple arguments may be passed via the "sender" LocalConnection call to send() that will be accepted by the prescribed method on the "receiver" object.

I have three source files in this example. The first source file (ExampleSwfObjectPage.html) is the HTML page that uses SWFObject to host both the "sender" Flash application and the "receiver" Flash application. The second source file (DriverDataGrid.mxml) is in MXML and represents the Flex application that will be the "sending" SWF. The third source file (TargetDisplay.mxml) is also MXML and represents the Flex application that will be the "receiving" SWF.


ExampleSwfObjectPage.html

This HTML page uses SWFObject to embed both the "sender" and "receiver" Flash applications (.swf files) in the page. When rendered, they will be obviously differentiable by their different color of backgrounds. The code for this HTML page is adapted from the SWFObject documentation on embedding SWF content with static publishing. Here is the HTML code.


<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<!-- Template adapted from http://code.google.com/p/swfobject/wiki/documentation. -->
<head>
<title>CSS 2008 Flex-to-Flex Example: Driver</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript">
swfobject.registerObject("sourceSWF", "9.0.0", "expressInstall.swf");
swfobject.registerObject("targetSWF", "9.0.0", "expressInstall.swf");
</script>
</head>
<body>
<center><h1>CSS 2008 Flash-to-Flash Example: Driver Page</h1></center>

<div>
<object id="sourceSWF"
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
width="100%" height="250">
<param name="driver" value="DriverDataGrid.swf" />
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" data="DriverDataGrid.swf"
width="100%" height="250">
<!--<![endif]-->
<p>Unable to find or render the DriverDataGrid.swf Flash Application</p>
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>
</div>

<div>
<object id="targetSWF"
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
width="100%" height="100">
<param name="target" value="TargetDisplay.swf" />
<!--[if !IE]>-->
<object type="application/x-shockwave-flash" data="TargetDisplay.swf"
width="100%" height="100">
<!--<![endif]-->
<p>Unable to find or render the TargetDisplay.swf Flash Application</p>
<!--[if !IE]>-->
</object>
<!--<![endif]-->
</object>
</div>

</body>
</html>



DriverDataGrid.mxml

This MXML file will be the "source" or "sending" SWF in the example The callOtherFlashApp() method makes use of the LocalConnection instance to call the send method to invoke a method name "displaySelectedPresenter" on a connection named "cssConnection" and pass that method a single String argument that of the full name and organization of the speaker as constructed from the selected item in the DataGrid.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
applicationComplete="setUpSenderConnection();">
<mx:Script>
import mx.controls.Alert;
private const connection:LocalConnection = new LocalConnection();

/**
* Set up connection.
*/
public function setUpSenderConnection():void
{
connection.addEventListener(StatusEvent.STATUS, onConnectionSendStatus);
}

/**
* Call another, external Flash application.
*/
public function callOtherFlashApp():void
{
const selectedPresenterName:String =
dataGrid.selectedItem.firstname + " " + dataGrid.selectedItem.lastname
+ " of " + dataGrid.selectedItem.organization;
connection.send("cssConnection", "displaySelectedPresenter", selectedPresenterName);
}

/**
* Handle status associated with communicating with other Flash application.
*
* @param event Status event associated with sending to another SWF.
*/
private function onConnectionSendStatus(event:StatusEvent):void
{
switch (event.level)
{
case "status":
trace("Use of LocalConnection.send() succeeded");
break;
case "error":
Alert.show(
"The LocalConnection.send() call failed: "
+ event.code + "\n"
+ event.toString() );
break;
}
}
</mx:Script>
<mx:XMLList id="presenters">
<presenter>
<lastname>Johnsson</lastname>
<firstname>Dan Bergh</firstname>
<organization>OmegaPoint AB</organization>
<url>http://softwaresummit.org/2008/speakers/johnsson.htm</url>
</presenter>
<presenter>
<lastname>Kaplan-Moss</lastname>
<firstname>Jacob</firstname>
<organization>Whiskey Media</organization>
<url>http://softwaresummit.org/2008/speakers/kaplan-moss.htm</url>
</presenter>
<presenter>
<lastname>Lan</lastname>
<firstname>Ikai</firstname>
<organization>LinkedIn</organization>
<url>http://softwaresummit.org/2008/speakers/lan.htm</url>
</presenter>
<presenter>
<lastname>Marx*lt;/lastname>
<firstname>Dustin</firstname>
<organization>Raytheon Company</organization>
<url>http://softwaresummit.org/2008/speakers/marx.htm</url>
</presenter>
<presenter>
<lastname>Morrill</lastname>
<firstname>Dan</firstname>
<organization>Google</organization>
<url>http://softwaresummit.org/2008/speakers/morrill.htm</url>
</presenter>
<presenter>
<lastname>Moskowitz</lastname>
<firstname>David</firstname>
<organization>Productivity Solutions, Inc.</organization>
<url>http://softwaresummit.org/2008/speakers/moskowitz.htm</url>
</presenter>
<presenter>
<lastname>Opstvedt</lastname>
<firstname>Hermod</firstname>
<organization>DnB NOR</organization>
<url>http://softwaresummit.org/2008/speakers/opstvedt.htm</url>
</presenter>
</mx:XMLList>

<mx:Panel id="mainPanel" width="75%"
title="CSS 2008 DataGrid Example">
<mx:DataGrid id="dataGrid"
width="100%" height="100%"
rowCount="5" dataProvider="{presenters}"
itemClick="callOtherFlashApp();">
<mx:columns>
<mx:DataGridColumn dataField="lastname"
headerText="Last Name"
width="75" />
<mx:DataGridColumn dataField="firstname"
headerText="First Name"
width="75" />
<mx:DataGridColumn dataField="organization"
headerText="Organization"
width="100" />
<mx:DataGridColumn dataField="url"
headerText="CSS URL"
width="200" />
</mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>



TargetDisplay.mxml

The second Flash application will be the "receiver" and will display the presenter name and organization passed to it. This is represented in the code for TargetDispaly.mxml that is shown next.

The setUpReceiverConnection() method demonstrates how easy it is to tie the receiver to the LocalConnection. The provided connection name matches exactly the String provided as the connection name provided in the sender's invocation of LocalConnection.send(). As importantly, there is a method in this SWF application called displaySelectedPresenter and this name matches that provided in the sender's send() method. This method also accepts the single String with presenter name and organization and processes that passed-in String.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
backgroundColor="#CCFFCC"
applicationComplete="setUpReceiverConnection();">
<mx:Script>
import mx.controls.Alert;
private const connection:LocalConnection = new LocalConnection();

/**
* Set up connection.
*/
public function setUpReceiverConnection():void
{
connection.client = this;
connection.connect("cssConnection");
}

/**
* Display the provided presenter's name.
*
* Name of presenter to display.
*/
public function displaySelectedPresenter(selectedPresenterString:String):void
{
try
{
targetText.text = selectedPresenterString;
}
catch (error:ArgumentError)
{
Alert.show("Error trying to connect to LocalConnection.");
}
}
</mx:Script>

<mx:HBox>
<mx:Label fontSize="20" text="Selected Presenter: " />
<mx:Label fontSize="20" id="targetText" />
</mx:HBox>
</mx:Application>


With the code in place, we can move onto what the single page with two SWF applications looks like. The following screen snapshots show the single page with two SWF applications running on the Google Chrome web browser. The different colors of backgrounds indicate the two different SWF applications.

The first screen snapshot shows the application after it has been loaded, but no presenter has been selected.



The second screen snapshot demonstrates the application after a row representing a speaker in the DataGrid has been selected (clicked on). The SWF with the DataGrid then communicates behind the scenes to the SWF with Flex Labels and that receiving SWF application renders the selected presenter's name and organization.



This simple example just scratches the surface of SWF-to-SWF communication and the possibilities of the LocalConnection class. There are many more security considerations and uses of send() and connect() depending on your usage of the LocalConnection.


Important Notes

The blog entry SWF-to-SWF Communication Issue blog entry talks about an issue associated with use of LocalConnection for SWF-to-SWF communication that unintentionally crosses different web browsers. This same blog entry also references this example of communication between SWF files using LocalConnection. In his blog entry, Ryan Gorer recommends building a unique String in JavaScript for the connection and supplying that to the sender's send and the receiver's connect methods.

According to the ASDoc documentation, the LocalConnection.send() method is limited to sending 40 KB or less of parameter data. When this is exceeded, an error message like that shown in the following screen snapshot is encountered:



The good news is that this is a relatively high upper limit on the arguments size.

Another blog entry on Flash-to-Flash communication is available in the blog entry How To Communicate Between Multiple Flash Applications with LocalConnection.

3 comments:

SystemTool said...

Can you explain the SWF-SWF communication in different machines using LOCAL CONNECTION class itself.

Dustin said...

SystemTool,

According to the documentation, "LocalConnection objects can communicate only among files that are running on the same client computer," so you should not use LocalConnection for communication between different machines.

You might want to consider NetConnection or a host of other alternatives for communication between different machines.

Dustin

David Alves said...

What about communication between two different applications on the same computer then. Should that work? I mean separate applications nothing embedded, nothing loaded. Just two apps executed in the same computer?