Flex Mobile Development: storing data locally

One way to improve the user experience for mobile applications is to save data locally. This technique is important for desktop applications and it is even more important for mobile applications (mostly because the network connection is expensive, slower, or intermittent).

When using applications such as mail or Twitter clients local storage enables users to see the messages or tweets loaded in the previous session. And while you are seeing these “old” items the application can pull in newer items behind the scenes. The main idea is to not to make the user stare at an empty screen each time he opens your application.

A second benefit of this technique is that your application will feel much more responsive. As I said it is possible that the user has a slow Internet connection and downloading 12 small pictures and some descriptions could be visibly slow. Local storage can offer a “performance” boost especially for applications that access data that changes rarely.

Depending on your specific use case you may want to save locally:

  • media assets (images, MP3 files, video files)
  • text or HTML resources
  • XML files
  • ActionScript data structures (an array of ActionScript value objects for example)

So how can you store data locally in an AIR mobile application (Android, PlayBook, and iOS)? You have almost the same methods as in AIR desktop applications (on the desktop you can use the encrypted local store to save ActionScript objects):

  • Use the embedded SQLite database – obviously perfect for data that are a good match for a relational database
  • Save ActionScript objects or collections of objects in files – serialize the objects into a file and read back when needed. Or you can use SharedObject to do the same
  • Save binary assets directly into files – perfect for saving images or videos locally

I won’t go into details about these three techniques. Almost three years ago I wrote a blogpost that explains each technique (back then it was only AIR for desktops, but remember that apart from the encrypted local store you can use the same approach on mobile or desktop). So click here if you want to learn how to use these techniques.

There is one aspect I do want to highlight, however. When you want to save data locally using files you should always use the application storage folder. This folder is outside of the application installation folder. So you can update the application without worrying about the saved data. Here is a simple example for creating a new file inside the application storage folder:

var file:File = File.applicationStorageDirectory.resolvePath("new-file.png");
var fileStream:FileStream = new FileStream();
fileStrean.open(file, FileMode.WRITE);
fileStream.writeBytes(data, 0, data.length);
fileStream.close()

Flex mobile support for persistence

You probably already know about the persistence support of the Flex mobile framework. If you save your information to the data property of your View pages and set the persistNavigatorState attribute in the main application file to true, then all the information will be automatically saved when the application is closed and restored when the application is re-opened.

So here is a simple example. Suppose that you use this data model in your application:

package {

[Bindable]     
class User {
    public var firstName:String;
    public var lastName:String;   
}
}

Next, you turn on the persistence support in the main application file:

<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    firstView="views.HomeView"
    persistNavigatorState="true"/>

And finally you use the data property to save the current instance of User. Bellow is the first View of the application. It uses bidirectional binding for displaying the User first name and last name properties.

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
   xmlns:s="library://ns.adobe.com/flex/spark"
   title="HomeView"
   viewActivate="onViewActivated()">
<fx:Script>
 <![CDATA[

   private function onViewActivated():void {
      // if it is the first time the application runs t
      // here is no data
      if (!data) {
         // create a new User instance and
         // save it to data property
         data = new User();
      }
   }

 ]]>
</fx:Script>
 <s:layout>
     <s:VerticalLayout/>
 </s:layout>
 <s:Spacer height="10"/>

 <s:Label text="First Name:"/>
 <s:TextInput width="100%" text="@{data.firstName}"/>
 <s:Label text="Last Name:"/>
 <s:TextInput width="100%" text="@{data.lastName}"/>
</s:View>

When you run the application for the first time you won’t see any information in the two text input boxes. However if you type in something and then close the app and re-open it, you will see the information you typed during the previous session.

When is the data property of a View saved to disk? Every time the application is closed or a View is destroyed/replaced by a new View. In terms of events right after the View deactivate event is triggered.

When is the data property of a View recreated if persistence is turned on? Right before the View activated event is thrown.

The data property of each View is saved to disk using flash.net.SharedObject and the object that does this is the ViewNavigator instance accessible in any View class by using the navigator property.

If you want to be sure that the data property is persisted you can call navigator.saveViewData() in your View. Why would you want to trigger the saving process manually? Because in the unlikely event the app is closed by the operating system due to low battery or memory events you cannot be sure that the normal event flow for closing the app will be followed. By calling navigator.saveViewData() manually each time the user updates something you can make sure that nothing will be lost.

RegisterClassAlias

When you serialize ActionScript objects to files or use SharedObject the original data type is lost when you deserialize them. The same applies when using the data property of the View because, under the hood, SharedObject is used for storing the data. If you want to keep the custom data type, all you have to do is to use registerClassAlias() method.

Why? Because the serialization is done using the AMF3 format (Action Message Format) and if you want to preserve the class information for the objects you serialize/deserialize then you have register the class alias of the class object. If you don’t do this, when you decode the objects you’ll get anonymous objects.

In a previous section I used a custom data type User. If I want to make sure that the data type is preserved I can register this class in the main application file (this way I make sure that the class is registered before any decoding is performed):

<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
 xmlns:s="library://ns.adobe.com/flex/spark"
 firstView="views.HomeView" persistNavigatorState="true">

<fx:Script>
<![CDATA[
   import flash.net.registerClassAlias;

   registerClassAlias("User", User);
]]>
</fx:Script>

</s:ViewNavigatorApplication>

Conclusions

On Android you have to remember to set the appropriate permission in the application configuration file if you want to write files to disk (android.permission.WRITE_EXTERNAL_STORAGE). Also take into consideration how much space the data you want to saved will occupy. You should let the user know if you plan to consume GB of space.

8 thoughts on “Flex Mobile Development: storing data locally

  1. Mihai, this is a good stuff but what do you think about this situation:

    I have an application that uses data from webshop site. I have 100 products with 100 images. I’ll receive those images on my device and store it locally but how will I decide are these images or data obsolete?

    How to decide when this “cache” is out-of-date :) ?

    My idea was to receive data only for products which are modified after user’s last attempt to receive data.

  2. @Ivan Ilijasic

    This could work. Just save the date, on the server side, for each product when is updated or added.

    On the client side (mobile up) save the date for the last update.

    Each time the application is started you make a request sending the last date of an update. Server compares this date with the dates stored for the products and send the updates or nothing :)

    Easy :)

    Mihai Corlan

  3. Pingback: Weekly Developer Journal: August 14 – August 20

  4. There is another option how class could be registered for pesristance. Instead of registerClassAlias(“User”, User), you could use following class declaration:

    [RemoteClass(alias=”User”)]
    class User {

  5. How do I save a image I download from a webserver using the PersistenceManager.
    I made a this, but when I want to display the image I get and error. Do I need to uses any function to read it back to a Image view?

    package {

    [Bindable]
    class User {
    public var Name:String;
    public var MyImage:Object;
    }
    }

  6. Hi Mihai,

    I have a question about the applicationStorageDirectory. Will the data you store in there survive an app update?

    How about when an app is removed from the device, is the applicationStorageDirectory and its contents also removed?

    Thanks,

    Tim

  7. Hi,
    Is there a way to permanently save data on a mobile device?

    I want to store a unique id on the device, so when a user removes the app and reinstalls it, i want to connect that user to the same online database record.
    This is for a credit system, so when user reinstalls i want to give him the credits back. (after all, he payed for them)

    Any ideas?

  8. HI Tim,
    I think the data which is stored in the Application Storage Directory will be removed when the application is removed . I tired to store my login credentials in a table using sqlite database inturn which is stored on the device using File.applicationStorageDierctory.resolvepath()…. , when ever I uninstall the my app the entire data in the above path gets removed .
    Hope it will be useful.
    Thanks,
    Nagaraju

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>