Caching data locally to work offline in AIR applications
These days here in Comtaste, I'm very busy for a consulting activity to support a project for an Adobe's Italy partner (I'm under NDA so I can't do names), working on a enterprise desktop AIR application (that uses Java, Oracle Form and Hibernate). The project is a porting of an ActionScript 3/J2EE web application, so what we're doing is to write ActionScript 3 classes to add AIR desktop functionalities.
A sad note. This project is the reason why I won't be able to come to the onAIR tour in Milan :( damn !
We're spending a lot of time defining the state of the application when it goes offline. In fact the AIR project will work in online as well as offline mode (it's what Adbe calls Occasionally Connected Application).
The application must provide most of the functionalities when it's on offline status: reading and writing data, loading assets, saving data etc.
Actually AIR comes with a lot of APIs to reach these goals: the service monitor framework, the SQLite support, the I/O classes and more.
What we're developing is a series of ActionScript 3 classes to synch a database from local to remote using SQLite (local) and Oracle (remote) and a system that checks and monitors the network connectivity to cache external assets from the web server to the local client.
I can't post the code we're working on, but I want to share some simple and effective tips for caching assets when the AIR application goes offline.
The AIR SDK provides the service monitor framework APIs that consti of the ServiceMonitor class plus two subclasses: URLMonitor and SocketMonitor.
The service monitor framework is not included in the standard AIR SDK so if you're using Flash or AJAX you have to import the libraries into your package. If you are using Flex Builder 3 the IDE will embed the framework automatically.
You can read a lot about this framework on the AIR documentation.
The service monitor framework allows developers to test and monitor HTTP based service using the URLMonitor subclass. The usage is pretty simple. You specify two parameters in the constructor of the class: the URLRequest object and the
Invoking the start() method of the URLMonitor class a listener will be created and will check and monitor the URL specified for the netire life of the application. If the status of the netwrok connectivity will change, the STATUS event will be raised.
So we can use this event accord ???? to get the status of the connection through the available property of the URLMonitor class.
import air.net.URLMonitor;
import flash.events.StatusEvent;
import flash.net.URLRequest;
monitor = new URLMonitor(urlRequest);
monitor.start();
monitor.addEventListener( StatusEvent.STATUS, onStatusEvent );
private function onStatusEvent( event:StatusEvent ) : void
{
if (monitor.available)
{
// You're connected
// Load your assets from your remote server
} else
{
// You're not connected
// Open the assets locally
// Change the state of the application to offline status
}
}
Once we get the status of our connection and getting notified by any network connectivity change, we can write the methods to save and read the assets locally.
import flash.filesystem.*;
private var myFile:File;
private var stream:FileStream;
[Bindable]
private var xmlData:XML;
private var myAC:ArrayCollection;
private var xmlResult:XML;
public function init():void
{
myFile = File.documentsDirectory;
myFile = myFile.resolvePath("remoteData.xml");
openXML();
}
private function openXML():void
{
stream = new FileStream();
if (myFile.exists) {
stream.openAsync(myFile, FileMode.READ);
stream.addEventListener(ProgressEvent.PROGRESS,progressHandler);
stream.addEventListener(Event.COMPLETE, completeHandler);
} else {
// The file does not exist locally
// Download it from the remote server using a RPC class
myHTTP = new HTTPService();
// Insert your domain name
myHTTP.url = "http://88.149.156.198/develop/xmloutput/index_xml.php";
CursorManager.setBusyCursor();
myHTTP.send();
myHTTP.addEventListener(ResultEvent.RESULT, resultHandler);
myHTTP.addEventListener(FaultEvent.FAULT, faultHandler)
}
}
private function progressHandler(event:ProgressEvent):void
{
var str:String = "Byte total : " + event.bytesTotal + "/n Bytes Loaded : "+ event.bytesLoaded;
asincTxt.text = str;
}
private function completeHandler(event:Event):void
{
xmlData = XML(stream.readUTFBytes(stream.bytesAvailable));
stream.close();
// Create the Collection class to handle the data
}
private function saveData():void
{
xmlData = XMLResult.toXMLString()
var outputString:String = '<?xml version="1.0" encoding="utf-8"?>\n';
outputString += xmlData.toXMLString();
outputString = outputString.replace(/\n/g, File.lineEnding);
stream = new FileStream();
stream.open(myFile, FileMode.WRITE);
stream.writeUTFBytes(outputString);
stream.close();
}
private function resultHandler(event:ResultEvent):void
{
xmlResult = new XML(event.result);
myAC = event.result.root.user;
CursorManager.removeBusyCursor();
}
private function faultHandler(evt:FaultEvent):void
{
var errors:String = "Some errors occurred \n";
errors += "\n Fault Code is : \n" + evt.fault.faultCode;
errors += "\n Fault Detail is : \n" + evt.fault.faultDetail;
errors += "\n Fault String is : \n" + evt.fault.faultString;
mx.controls.Alert.show(errors);
CursorManager.removeBusyCursor();
}
With this simple approach you can cache application's assets locally and then access it when the AIR application will go offline. You can do that for caching images, xml data, actionscript objects, databases. Then you can synch the local assets sending them to the web server.




















Great example Marco!!!. Just in this days I'm working in a very similar project, I will try to get the new AIR books to approach a hand on reference.
I read your Flex Solutions book and it's an amazing book to get started with Flex, if I didn't have the book I can't do all things that I doing in my job.
Thanks a lot!!!.
Posted by: Mariano KIWO Carrizo | June 14, 2008 at 06:43 PM
Hey, I am doing a very similar project. I am curious about SQLite though. From what I've read about SQLite, it is limited - but was it enough to handle the data that you have? The "remote" DB is close to 1GB in data. Will SQLite handle that? I want to use SQLite because I would not have to deal with a local Client-server architecture on a personal computer. However, that would require a lot of SQL statements written in AS3. On the other hand, I can setup a local webserver, run BlazeDS, set up a local sql-server of some sort, and the AIR application. It seems like a lot of "stuff" on a personal computer. Help?
Posted by: Tin-Tin | June 17, 2008 at 02:35 AM