In my current AIR project I have a need to store persistent user data across sessions. Because the amount of data is fairly small, but the objects are complex, a SQL Lite database wasn’t ideal. I also couldn’t use standard SharedObjects because of the file size limitations and the fact that crashed our IDE (more on that in a later post).
The solution I came to was to make my own SharedObjects, or rather custom files using the AMF data format. It was actually much easier than I had initially feared. I got my start by reading this article by Ted Patrick. (Unfortunately, the article is hosted on a Sys-Con property, and I’m not a fan of their tatics.)
Ted’s article is short and sweet and shows simply how to read and write AMF-encoded objects from/to a ByteArray. Since we can easily save ByteArrays to disk and read them out again later, this is a perfect solution to the storage needs.
So, this approach works great if you are only using simple Objects made up of Flash Player core types. Things get a little more complicated when you need to save complex custom objects. AMF doesn’t automatically recognize your custom objects. This effects all AMF transport methods, including SharedObject, LocalConnection and Remoting.
In order to inform AMF which classes it should recognize, you need to use the registerClassAlias() method:
import com.webdeely.MyCustomClass;
// pass in the path to the class definition and a
// reference to the actual Class
registerClassAlias("com.webdeely.MyCustomClass", MyCustomClass);
var mcc:MyCustomClass = new MyCustomClass();
mcc.message = "Hello World!";
mcc.favoriteNumber = 12;
mcc.userName = "MiltonB";
// ByteArray will now recognize all of your custom class' properties,
// and encode them to AMF
var byteArray:ByteArray = new ByteArray();
byteArray.writeObject( mcc );
// ...and they can be decoded directly back to your class!
var mcc2:MyCustomClass = byteArray.readObject() as MyCustomClass;
Ok, so now we have the encoding and decoding working properly, but we still need to save the file to disk in order for any of this to be useful. For this we’ll use AIR’s File and FileStream classes:
import flash.filesystem.FileStream;
private function writeFile( bytes:ByteArray ):void
{
// save our data to the user's desktop, in a file called "test.sol"
// btw, 'sol' is not a required extension, in fact you don't need an extension
var file:File = File.desktopDirectory.resolvePath("test.sol");
// Create a file stream to write stuff to the file.
var stream:FileStream = new FileStream();
// Open the file stream and write the data
stream.open( file, FileMode.WRITE );
stream.writeBytes(bytes,0,bytes.bytesAvailable);
stream.close();
}
private function loadFile():ByteArray
{
var bytes:ByteArray = new ByteArray();
var file:File = File.desktopDirectory.resolvePath("test.sol");
var stream:FileStream = new FileStream();
// Open the file stream in read mode
stream.open( file, FileMode.READ );
// read the bytes directly into the ByteArray
stream.readBytes(bytes, 0, stream.bytesAvailable);
stream.close();
return bytes;
}
Hopefully this will be helpful to anyone faced with similar challenges. I have found the de/serialization of the AMF objects to be extremely fast, reliable and convenient. It works wonders and was a very quick switch over from the standard SharedObject process.
I have put together a simple Flex project file for an AIR app that allows you to experiment with the code here. You can download the package here.
#1 by Bálint Magyar on April 23, 2009 - 1:36 am
Thank you very much, that was exactly what i was looking for. You saved me some wasted hours!
#2 by Arun on June 29, 2009 - 12:48 pm
Thanks for your post. Its really helpful.
I was looking for a way to deserialize web browser created shared objects in an AIR application using fileStream and ByteArray (something similar to .minerva project at http://blog.coursevector.com/minerva). Do you have any instant solution to get it solved?
#3 by Arun on June 29, 2009 - 12:54 pm
” I was looking for a way to deserialize web browser created shared objects ” –
Flex Application running in web browser created a shared object data in the directory structure
[ex: C:\Documents and Settings\xyz\Application Data\Macromedia\Flash Player\#SharedObjects\5RP3MMUQ\#localhost\BrowserFlexSharedObjects\bin-debug\BrowserFlexSharedObjects.swf\cachetest.sol]
To be read and converted to equivalent AS Object in an AIR App through File, FileStream, readBytes(), ByteArray…
#4 by Chris Deely on July 23, 2009 - 7:26 pm
@Arun
Sorry for the delayed response. I don’t see why you wouldn’t be able to deserialize objects stored in browser-created SharedObjects.
The real problem is locating the files on the user’s hard drive. For security purposes, SharedObjects are designed to only be accessible to the domain (website) that created them. They are a lot like browser cookies in that way.
The Flash Player automatically restricts access to these files when running Flash content in a browser. In an AIR app, however, you have full access to the entire file system, even these SO files.
As long as you can locate the file, you should be able to read it. This leads to some very shaky ground as you could access data stored from other websites.
But that is the risk inherent in installing software on one’s machine!
#5 by Quentin on September 18, 2009 - 2:02 am
Hey there!
Interesting post.
Just like Arun I wanted to read/write native SOL files in an AIR app, but realized it was not really possible. Or at least I didn’t find how…
When you open a (native) SOL file (FileStream.open) and access its content (FileStream.readObject) you only get a null object. If anyone has a clue, please share it!
#6 by Chris Deely on September 19, 2009 - 3:37 pm
@Quentin
I’ll have to experiment with reading browser SharedObjects, I see no reason why it wouldn’t work.
Are you storing any complex objects in the SO? Meaning anything other than Flash Player native obects (Number, Boolean, Array, etc).
If so, you need to register the class alias for each object you wish to retrieve from the SO.
#7 by Quentin on September 21, 2009 - 12:23 am
I am not storing anything actually, just trying to read it, first!