Figure 1, on the previous page, shows the code elements you will create in your application to implement the following pattern:
IGeneral). This includes functionality such as saving files or writing to SQLite. The GeneralFactory class is a Factory that knows how to create the Adobe AIR and web-specific concrete classes.AirProject project contains elements specific to the AIR project. It also references the MainCanvas.mxml component, defined in CommonProject.WebProject project contains elements specific to the AIR project. It too references the MainCanvas.mxml component, defined in CommonProject.You will implement the specific concrete classes (FlexGeneralImplementation and AirGeneralImplemenation) for both the AIR application and for the browser-based application.
The next section shows you how to create these projects in Flex Builder.
To get started, you'll need to create three projects in a new Flex Builder workspace. I'm assuming you're already familiar with creating projects in Flex Builder. (If not, visit the Flex Developer Center to get started with Flex.)
Create a Flex project named CommonProject. Create this project as a Web application, not as a Desktop (AIR) application. Leave the default settings for the main source folder (src).
This project is a Flex application that holds the application entry MXML component of your project. In the sample files that accompany this article, it's called CommonProject, and the main MXML component is MainCanvas.mxml. This is the main project where all your application code is going to go for both the web and Adobe AIR applications. This project should never reference any Adobe AIR APIs. This is very important; remember it, live it: No AIR APIs in this project.
CommonProject (which you created in step 2). This will share the source code from the common project (which will be important later on). Figure 2 shows what an appropriately added source path looks like. This project will have full access to Adobe AIR APIs.

Figure 2. The Flex Build Path dialog box.
Since both the Adobe AIR and web-based Flex versions of the application use the same user interface (UI), I'm going to use the MainCanvas.mxml file for all my main UI work. This is the place to start designing your application. In the sample, there's a button (Save File) that triggers the functionality to perform.
In the Adobe Air project's main MXML file, add the following
code bound to the applicationComplete event to add the shared MainCanvas.mxml
component:
private function onApplicationComplete():void { var can:MainCanvas = new MainCanvas(); this.addChild(can); can.labelMessage = "Loaded in an AIR Application "; }
This adds the Common Project's MainCanvas.mxml UI that
was created above to the WindowedApplication entry point of your Adobe
AIR application.
For the Web Project, the code is very similar; only the label message changes:
private function onCreationComplete():void { var can:MainCanvas = new MainCanvas(); this.addChild(can); can.labelMessage = "Loaded as Web Browser Flex Application"; }
You'll notice the following lines in the AirProject.mxml and
the WebProject.mxml files:
private static const onlyForCompilation:FlexGeneralImplementation = null;
or
private static const onlyForCompilation:AirGeneralImplementation = null;
This declaration of onlyForCompilation is
really important. The MXML compiler will remove your definitions of FlexGeneralImplementation and AirGeneralImplementation (described below) since your code doesn't directly reference them (it only
references the IGeneral interface ) and creates them dynamically. Without
this declaration, when the factory dynamically creates the object, it will fail
because it wasn't included in the compiled code. Be warned.
The IGeneral interface defines the functionality
that is to be abstracted out. All functionality that calls into specific Adobe AIR
APIs need to be defined here. For example, writing to SQLite, implementing
native windows, local file I/O, displaying system tray notifications, and so
on, will all need to be called through this interface. When it comes to the
actual implementation, the classes that implement this interface (FlexGeneralImplementation and AirGeneralImplementation)
will do all the hard work.
public interface IGeneral { function saveFile():void; function webOnlyFunctionality():void; function airOnlyFunctionality():void; }
The saveFile mehod is going to be the main method
demonstrating the decoupled functionality. The other two methods are added for
fun.
To get around compilation errors when compiling a Flex application that references code that relies on Adobe AIR specific APIs, I'm going to rely on a self-aware factory that creates objects that either rely on these Adobe AIR specific APIs or not. The factory design pattern is the key to making all this happen.
At it's most basic level, the factory pattern is an object-oriented creational pattern that creates objects without the client knowing about the specific class that is being created. In the case of this featured solution, the factory is aware of whether it's running inside of Adobe AIR or Flex, and it'll return appropriate classes that have specific Adobe AIR or Flex functionality which implement interfaces that our application understands.
The factory is responsible for creating objects. In this
example, the factory GeneralFactory is going to create either Adobe AIR
specific or Flex specific objects, based on whether it's running inside a web
browser or as a desktop application. It'll create FlexGeneralImplementation when running as a Flex application inside a web browser, or an AirGeneralImplementation when running on the desktop.
GeneralFactory is found in the CommonProject:
Import the IGeneral interface, where the functionality is
defined to provide either Adobe AIR or web-specific implementations:
import IGeneral; import flash.system.ApplicationDomain; import flash.system.Security;
Define the GeneralFactory class:
public class GeneralFactory { public static const NOT_SUPPORTED_IN_WEB_MESSAGE:String = "Not supported in web version.";
Create the getClassToCreate which dynamically inspects the
current ApplicationDomain.
This grabs the definition of the class to create, which is either the AirGeneralImplementation or the FlexGeneralImplementation,
and returns the class so it can be created.
static private function getClassToCreate(className:String):Object { var someClass:Object = null; someClass = ApplicationDomain.currentDomain.getDefinition(className); return someClass; }
Expose the generator method (getGeneralInstance).
This instantiates an object based on the class definition that was retrieved
above in getClassToCreate.
The generator is self-aware. It knows whether it's running inside Adobe AIR or
regular Flex, and appropriately requests the class to be created. The important
thing to note here is how to dynamically create a class at runtime based on a
string name.
static public function getGeneralInstance():IGeneral { var general:IGeneral; var cls:String = (isAir ? "AirGeneralImplementation" : "FlexGeneralImplementation"); var clsToCreate:Object = getClassToCreate(cls); general = new clsToCreate(); return general; }
Finally, implement isAir(), which defines the runtime context of
the application. Since the constant Security.APPLICATION is
only defined in the Adobe AIR framework, use the string "application" instead.
static public function get isAir():Boolean { return Security.sandboxType.toString() == "application" ? true : false; }
Above the interface IGeneral was defined.
Now it's time to implement the Adobe AIR and Flex project-specific code.
The AirGeneralImplementation class is very simple,
yet it demonstrates calling into an Adobe AIR specific API. The browseForSave()
brings up an operating system–specific dialog box prompting the user to save a
file locally. Calling this API from Flex can't happen—the code won't compile.
If you attempted to import flash.filesystem into a Flex project, you'd see
a big fat error message: "1172: Definition flash.filesystem could not be
found." This is why the function that calls browseForSave was
abstracted in this manner, so it will only compile in the Adobe AIR
application.
AirGeneralImplementation implements a couple of
other methods, like webOnlyFunctionality, which throws an error,
since it is not providing an implementation in the Adobe AIR application. The
same happens with airOnlyFunctionality, which doesn't have an
implementation in the FlexGeneralImplementation.
Since both the FlexGeneralImplementation and AirGeneralImplementation are very similar, I'm only going to show you the code for the Adobe AIR
implementation. Keep in mind, that the Flex implementation for saving a file
could be anything: it could send it as an e-mail or to the local machine by
proxying it through a web server.
The browserForSave is an Adobe AIR API for
interacting with native operating system dialog boxes for saving files:
public class AirGeneralImplementation implements IGeneral { public function saveFile():void { var f:File = new File(); f.browseForSave("Set sample to save file as"); } public function webOnlyFunctionality():void { throw new Error("Not Supported."); } public function airOnlyFunctionality():void { Alert.show("This is Adobe AIR Only Functionality"); } }
When the code is called, a variable of the IGeneral interface type is defined, and GeneralFactory creates an instance of IGeneral (which it knows how to create based on the runtime environment). Then, simply
call the saveFile method and the specific Adobe AIR or Flex implementation of IGeneral (AirGeneralImplementation or FlexGeneralImplementation)
will be exeutede. ViolĂ ! The Adobe AIR specific functionality was successfully
decoupled from the application.
private function saveFile():void { try { var general:IGeneral; general = GeneralFactory.getGeneralInstance() general.saveFile(); } catch (e:Error) { Alert.show("Ooops, something went wrong: " + e.message); } }
You've learned about dynamic object creation, simple factory pattern implementation, and calling methods on interfaces instead of classes. Seperating your code in this manner offers great design. It decouples functionality that is dependent upon specific Adobe AIR APIs from your main application, allowing you to generate an application for both the web and desktop.
A useful side-effect of implementing your code this way goes beyond just being able to compile both Flex and Adobe AIR applications from a single code base—which was the original goal.
Other benefits include:
To learn more about how to adapt a complex codebase to run also as an AIR application, view the presentation Architecting a shared codebase for browser and desktop or download the accompanying slides.
For more information about Adobe AIR, visit the product page.
For more inspiration, check out the sample applications in the Adobe AIR Developer Center for Flex, Flash, and HTML/Ajax.
To get started building Flex apps on AIR go to the Getting Started section of the Adobe AIR Developer Center for Flex or dig into Developing Adobe AIR applications with Adobe Flex. To dive right in and begin building AIR applications in Flex Builder, follow the simple steps in Developing AIR application with Flex on Adobe LiveDocs or explore popular Adobe AIR APIs by working with the AIR Quick Starts.