Flex 4.5 Mobile Data Handling

May 18, 2011By 49 Comments

Handling data in Flex for mobile requires a different way of thinking then traditional application programming. For instance, for many applications it’s important to persist (store) data such as if the user receives a call, hits a wrong button or otherwise has their application shut down. The mobile application’s Views also destroyed and recreated often so there are some things you need to be aware of in Flex 4.5 to handle the data between views even without your application being shutdown. So the two main times you need to worry about saving data are 1) Between application execution (closing and restarting app) and 2) Between views within your application. There are various ways to handle these scenarios.

Handling Data Between Views

There are a couple different ways to handle saving data while navigating between your views. First it’s important to understand the View life cycle. In Flex 4.5 only one View is active at any given time. By default all other Views will be destroyed and recreated when needed. However, there is a destructionPolicy property on the View class that is set to auto by default, but can be set to ‘never’ which will cause that View to be cached and hold it’s state until returning to it via a navigator.popView(). Consider the diagram below:

Specifying no destructionPolicy property or ‘auto’ for Views A and B is the default behavior, and means View A and B will get destroyed when the next view is pushed onto the stack, and recreated each time the view above it in the stack is popped (off). Setting destructionPolicy=”never” on View A and B will cause View A and B to be cached but just deactivated, then just added and activated when the pop view is called on the view in the stack above them.

It’s important to note though, that even if the destructionProperty is set to never on a View, if a pushView() is used to show it again it will always be recreated. The caching only occurs when the view above is popped off the stack to reveal a previous view. So in the following diagram, when View B is pushed for the 2nd time, it will get recreated and no data will be saved from the previous showing unless you use another method.

The normal order of events that occur by default (without setting the destruction property) can be seen in these trace statements from my application when a pushView() occurs from View 1 to View 2:

View 1 being removed in response to screen change (removing)
View 2 added to display list (added)
View 2 add event (add)
View 2 creation complete (creationComplete)
View 1 deactivated (viewDeactivate)
View 1 is being removed from display list (removed)
View 2 activated (viewActivate)

Using the data property

Another option (and more intuitive) way of saving data between views, is to use the data property on the View object. It will save the state of the data in-memory so when you return to your view via a push or pop it can be re-instantiated. You can set any type of object on the data property, including a custom class, such as a value object (like the Person class shown below for example). Also note that the data property in the view is instantiated and ready before the view is shown so you don’t have to worry about it being available. You can also return data from a popped view by overriding the createReturnObject() method on the View, such as:

override public function createReturnObject():Object
{
	return person;
}

where person is an instance of a class I have defined in ActionScript such as:

package model
{
	[Bindable]
	public class Person
	{
		public var person_id:int;
		public var userid:String;
		public var password:String;
		public var fname:String;
		public var lname:String;
		public var city:String;
	}
}

I can then access my returned object from the popped view by using (since the object type is actually ViewReturnObject, you have to specify the object property to get to the value):

data = navigator.poppedViewReturnedObject.object;

Persisting Data Between Application Execution

You have various options for persisting your data between application executions (after it’s been shutdown). Flex 4.5 has a built-in persistence mechanism where you can specify a property called persistNavigatorState on the ViewNavigatorApplication or TabbedViewNavigatorApplication to turn it on. There are also a couple of events to be aware of if you have the persistNavigatorState set to true. You can listen for the navigatorStateLoading and navigatorStateSaving events and perform any necessary actions in those handlers. So to turn this all on, your root application tag would look something like:

<?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.SampleDataPersistenceHomeView" firstViewData="{person}"
							persistNavigatorState="true" navigatorStateSaving="onStateSave(event)"
							navigatorStateLoading="onStateLoad(event)" >

When this is turned on, your application will save the state of your Views and data to a Local Shared Object with the name FXAppCache. Take a peek at the PersistenceManager.as source code from the Flex 4.5 SDK (located on my Mac under /Applications/Adobe Flash Builder 4.5/sdks/4.5/frameworks/projects/mobilecomponents/src/spark/managers) and you will see it creating this variable:

private static const SHARED_OBJECT_NAME:String = "FXAppCache";

After running your application in the Flash Builder emulator you can actually go take a look at what was saved by locating that shared object on your hard drive. The path to mine on my Mac is /Users/hollyschinsky/Library/Preferences/DataPersistence.debug/Local Store/#SharedObjects/DataPersistence.swf/FXAppCache.sol. Here’s a snapshot of what it creates:

Here’s what the actual saved data looks like after running my application with some data:

If you are going to be persisting a custom class, such as my Person class above, you need to handle the preinitialize event in the main application and register the class alias, such as:

protected function viewnavigatorapplication1_preinitializeHandler(event:FlexEvent):void
{
	flash.net.registerClassAlias("person",model.Person);
}

Your data will automatically persist by setting the persistNavigatorState property but if you would like to add more properties or access the persistence manager, you can do so by using the ViewNavigatorApplication or TabbedViewNavigatorApplication persistenceManager such as:

persistenceManager.setProperty("hollysProp","myProperty"); // set my own custom property
persistenceManager.getProperty("hollysProp"); // retrieve the value for my property

Or if you’re accessing it from a View you could use:

FlexGlobals.topLevelApplication.persistenceManager.getProperty("person"); //Retrieves my custom Person class object that was persisted

It’s especially useful to open the debugger and take a look at the persistenceManager variable. For instance, here’s a screenshot of mine showing my custom properties:

You can also see that two additional properties are also saved by default, the navigatorState and the versionNumber. The navigatorState is used to restore the views to their previous state.

The main con with the use of the default PersistenceManager is that the data is not encrypted. You are free to define a custom persistence mechanism, and there’s the IPersistenceManager interface available for just that. A great implementation might be to use an encrypted local store for getting/setting sensitive data. It’s also more useful for saving more bits of data. If you have a lot of data to be saved you would want to use SQLite. Christophe Coenraets has a great article that walks through creating a mobile application that writes to a SQLite database here.

Here are a couple of useful links to check out related to this post:
http://custardbelly.com/blog/?p=220
http://opensource.adobe.com/wiki/display/flexsdk/View+and+ViewNavigator

Filed in: Adobe FlexFlash Builder 4.5Flex 4.5Flex MobileMobile Development Tags:

About the Author ()

Comments (49)

Trackback URL | Comments RSS Feed

  1. Jon Webb says:

    Excellent post!

    Another issue with persistance that may be overlooked is when upgrading apps: the LSO will still exist from the previous version of the app, but perhaps without any new properties you have added in the new version.

    • devgirl says:

      Thanks Jon :)! And thanks for the heads up on the upgrade. I guess you would just manually delete it then in that case so it gets recreated when it’s run again? Thanks! Holly

  2. A question: Whenever I’ve seen Data being used, it’s always as an argument to the next view. Ie, “Flex, load the next view and let me pass some arbitrary data to it.” In your blog, you speak of it almost in the reverse. “I have a view and I want to save something so that if I’m destroyed, when I come back I still have it.” That is a different implication then I get from the docs.

    I’ve done tests before where in my viewActivate I’ll check for the existence of data, and if it isn’t there, I’ll create it as an Object. I can then load a new view, and then return, and my data object persists.

    It worked – but I didn’t know if Adobe meant for us to use it that way. It felt like data was _always_ meant as a ‘incoming argument’ only.

    Are you saying otherwise?

    • Hi Ray! Regarding your question about data.. It’s a little bit of both. I don’t think of it as an incoming argument only myself. I think of it more as a property on the view itself that is maintained between navigation because it’s automatically persisted in memory so it’s state can be restored when the view is recreated. But it is also used as a mechanism for passing data to views by setting the data property of the new view to whatever you passed in. If you look at the ViewNavigator source code in the SDK you can see that the data is saved off in a ViewDescriptor object and restored when it’s created. Of course this is all my interpretation of how it works :) Hope it helps! Holly

  3. Great post!
    I may translate this post into Portuguese (Brazil) and publish in my site? With the references. :)

  4. nasher says:

    how and when an end-user will enjoy the flexy stuff of fully optimized API across all Microsoft Apps?
    making new folder with name:01> right-clicking >options{the flex stuff} inherit parent folder name and properties> then all sub-folders have the same properties passed to them in one step!
    odd__but still relevant to flex potentials!

  5. nasher says:

    incremental naming of course!
    folder-name:01>SubFldrs>
    :01_01
    01_02
    01_03

  6. Brandon says:

    Thank you for this post! It was extremely helpful.

  7. Darin Weakley says:

    Thanks for the write-up! I guess I had nothing to do with the need for creating this, huh? ;-)

  8. Sydney says:

    I’m having a hard time getting data passed into my first view using firstViewData. I’m doing something very similar to your “person” object. I’m setting an object on navigatorStateLoading and then binding firstViewData to it. Data on my first view is always null.

    Do you have a code sample handy?

    • Sydney says:

      You know what….after I deleted FXAppCache.sol and cleaned FB it started working. I’m assuming I was in some kind of weird cached state. Lesson learned!

      • Hi there Sydney, I actually ran into that as well and should have mentioned that in my post. If you see funky results, clear the data or manually delete the LSO and it should fix it. Thanks :)!!

  9. Tomas says:

    Hi,
    is there way how to store (leave) data after uninstalling application? For example in case i want to release some trial version of application. Because if user uninstall and install application again i need to read from somewhere that this app was already installed before. I do not want to use some freely accessible directories like Desktop/User/Documents directories. Is there any mechanism for this?

  10. Steve says:

    Hi Holly,

    Thankyou for putting so much effort into your blog it’s helped me a lot (I’m new to flex only 2 weeks in).

    I’ve implemented your destructionPolicy=”never” idea mentioned above and have struck a minor problem.

    Basically I’ve got a list (using custom itemrenderer) with an IndexChangeEvent that push’s to a detail view. On the detail view I have a back button which pops back to the list.

    With above everything works perfectly until you push then pop and then rotate the device. When the device is rotated after the push and pop the width and height of the list doesn’t seem to adjust it’s size any more. So if I push and pop in portrait then rotate to landscape the list stay’s at the size it was in portrait. The opposite is also true… If I launch the app then rotate to landscape it works then push and pop and then rotate to portrait the list stays in landscape.

    I’ve removed the destructionPolicy and tested without it and everything works as I would expect.

    Is there a ‘trick’ I can use that will resolve this as the list I’m displaying is quite large and takes more than 5 seconds to build when I pop back to it without the destructionPolicy set to never?

    Best Regards

  11. Steve says:

    Hi,

    I also posted above question in the Adobe forums and got this answer to my question.

    https://bugs.adobe.com/jira/browse/SDK-30128

    So from what I read I just need to wait until the next build of the sdk occurs and it should resolve my issue.

    Cheers

  12. marius says:

    Nice article

    for those who try to access the returned object from a pop operation, they should save it on addedtostage or something, because on the view activate handler is already too late

  13. marius says:

    Gor a question: why the data is not persisted in debug mode?

  14. Roger says:

    Hi, your articles really help me a lot.

    I have a question now, how can I reload a mobile application (AIR) using the code with FLEX4.5? I found many articles that using “URLRequest(Application.application.url)” to reload the program, but it seems doesn’t work in FLEX4.5. So,do you have any suggestions or code sample?

  15. steve says:

    Hi, I have a TabbedViewNavigatorApplication with 3 views(ViewNavigator),
    I can use the navigator.pushView(view,dataobject) from actionscript to navigate to another view with a data object of my choice.
    How do I pass a data object of my choice when the user clicks on the default tab button along the bottom to switch views?

    • Hi Steve! You can pass data using the firstViewData property on the ViewNavigator object, such as:

      <s:ViewNavigator id="vn2" label="View2" width="100%" height="100%" firstView="views.View2View" firstViewData="{s}"/>
      

      Hope that helps!
      Holly

  16. Steve says:

    Hi,

    I’m struggling here with quite the opposite of aforementioned view destruction.

    I’ve got A B C D views simular to your diagram above and A and B both contain spark lists.

    On each screen I have a back button and also an index change event on the lists.

    In each of these events I change the current state to a completely blank state and also set this.stage.focus = null and listname.selectedIndex = -1;
    Then I call a push for both forward and backward transitions which from my understanding should destroy the view and all the objects/data.

    Well I’m finding that sometimes it destroys the view and sometimes it doesn’t… after about 12 forward and backward trips through the four views I end out with 4 copies of view B and 3 copies of view C still in memory according to the profiler (after running gc). Each time the view doesn’t get destroyed the valueObjects used in the list also stay in memory which causes my app’s memory to balloon…. An example of this is that list B always returns 34 records, after the 12 trips I have 136 Instances of the VO still in memory. I also have 1514 instances of StyleValidator (com.adobe.fiber.styles)..

    So if I’m loading a blank state prior to transition on every occasion and also setting focus off all objects, what more can I do to ensure the memory ballooning stop’s?

    Is it possible that I’m allowing the user to change into a new transition while the list is still loading…?

  17. Don Kerr says:

    Holly,
    Thanks for your firstViewData tip for Steve. :)

    I have a similar challenge, but I need to be able to programmatically change the selectedIndex of the tabbedApp AND push data to the first views in each tab.

    For example, firstView on tab1, has a list of jobs. User taps a job. I want to pass the selected job to the firstView on tab2 and set the tabbedNavigator.selectedIndex to tab2.

    issue I’m seeing is that when setting selectedIndex on the tabs it attempts to activate the view before the data is there. Can’t seem to be able to pass data and set the selectedIndex at the same time.

    In short, I’m doing sort of a wizard that changes the tabs as the user walks through the app, yet still allows them to return to previous tabs.

    Thanks!
    Don

  18. Don Kerr says:

    Holly,
    I decided to simplify the wizard-like process by stuffing the data selected in each view into FlexGlobals.topLevelApplication objects, then bind them to the firstViewData for the next tab.

    Then I just change tabs using tabbedNavigator.selectedIndex.

    This works great. But, if there is a more optimal way to do it, please let me know.

    Appreciate the help with firstViewData!
    Don

    • Hi Don! I have a blog post (or two if I break it up) coming this week that should help you with this and include a couple different ways of sharing data between tabs etc. I’ll share the project code as well. Hope to get something up later today. Let me know if it helps or makes sense when I do. Thanks!! Holly

  19. Pablo says:

    Great article and you have been a big help in getting some understanding on a few issues I’ve had on my current project. I have an issue trying to pass data between multiple views. If I pass data from one view to the next I can recall that data onto a second screen but if I skip a page I have no idea how to recall that information. LEts say I have list on page 1 with employee name, job and then want three button that read email, phone, address. I dont know how to pull information from the list onto the 3rd page since they wont be on the second… Please help :) Thank you again!

  20. Ken says:

    persistenceManager.save(); // force a save now otherwise, it sometimes won’t save on iOS

  21. Pablo says:

    Hey guys, thanks for the quick response, however, I am still having a little trouble trying to grasp how it all works, I think I get what you guys are saying in both ways but don’t know how to exactly apply a persistence manager to a list so that I can pull object data from said list to multiple views. I tried finding things on Adobe as well but it seems as tho their forums are temporarily down :/

  22. Gunnar Karlsson says:

    Hi Holly, Thank you for a consistently useful blog. I started using ViewReturnObject after reading about it here, and here’s what I found out: ViewReturnObject is destroyed after the creationComplete event is fired, so we can’t access the object in a creationComplete handler. We need to create a handler for the add event and access ViewReturnObject there. Net, we access ViewReturnObject first, then the data object. Hope this saves someone time. Cheers/Gunnar

  23. Oz says:

    Hello,

    I have a Windows AIR application in which I use a datagrid (added as a child to a panel tab) to display a debug log (log messages with columns for severity, message string, etc.). I ported my application to run on mobile platforms using the TabbedViewNavigatorApplication. I currently do not have any requirement for passing data between views.

    How do I go about implementing a similar (log of messages) functionality on the mobile platform? I could re-implement the datagrid as a DataGroup of formatted strings to display (say) the last N messages.

    Ignoring the performance implications of a datagrid for the moment (since I need the log for debugging), I have not been able to add the datagrid as a child of a ViewNavigator. Surely, I am doing something wrong. Even if I were to implement a simple DataGroup, how do I go about accessing the correct ViewNavigator to (say) addMessage() to that view?

    (Creating the view (lets call it “LogView”) with the destructionPolicy = “never” did not help).

    Thanks.

  24. Oz says:

    I modified the Log View class to be a singleton, and replaced the DataGrid with a DataGroup of list of strings. I can now log strings to it. However, the View’s initialize() routine is called only after the user clicks on the Log Tab (after which the strings are logged). It is functional for the purposes of developer debugging.

    If anyone has a better solution, do reply.

    Thanks.

  25. James says:

    Hi Holly,

    Can you point me to a simple example of how to use Flex 4.5 to open a local SQLite database, query it, and close it again. I can’t seem to find any examples that show how this works.

    James

  26. Kim Jensen says:

    Hi Holly,

    Just starting Flash Builder 4.5.1 and I learn how to use the PersistenceManager.
    One thing I’m not clear on is, if I use a package class like your Person, how will i get the data back in the TextInput. I have tried this but it does not work.
    var savedData:Object = persistenceManager.getProperty(“hollysProp”);
    myTextinput.text = savedData.city.toString();

    Kim

  27. David Wolpe says:

    Hey Holly –

    Great post, thanks. I’ve got an AIR app built with one related problem. It isn’t the data, but some of the assets, that are disappearing. When I click on my info tab, and then go back home, a couple of embedded buttons, as well as a spark hslider, disappear. I’ve got destructionPolicy=”never” but something else is wrong. Do you know how to get these assets to persist?

    Thanks!

  28. David Wolpe says:

    I’ve done some further investigation and it appears that if I set splashScreenMinimumDisplayTime=”1000″, this problem occurs. If I set it to 2000 or higher, it doesn’t. What’s more, setting some breakpoints indicated that the addedToStageHandler never fires with the shorter display time. Which explains why the assets don’t show. But is there some other event than addedToStage that should be used for mobile?

  29. David Wolpe says:

    Sorry I should have added that I’m now seeing the problem when the app initially loads – not just when I’m going from one view to another.

  30. Alessandro says:

    Hi Holly,
    there is a way to save also images?

    In a view I load an image from imageroll. If I save it with persistencemanager it works ok while I’m navigatin though app’s views. If I kill the app and run it again, I’m able to load my key with persistencemanager, but image are reloaded as simple Object and not as spark.components so they are not displayed. There is a way to convert and save them?

    thank you!

  31. Matt Allison says:

    Hi Holly, I’ve been struggling with my multi view application and navigating between the views. I’m using pop to go backwards and pushView to go forwards. As you mention in your post, pop preserves the component settings (a checkbox that has been selected for example) but pushView does not. I have a forward and backward button that calls the respective methods. My problem is that once you advance past a view and go back to it, the views which you have already visited are destroyed. For example, if I navigate from 1 to 2 to 3 and then move back to 1, any settings that I changed on 2 and 3 are lost visually. How does one create a linear navigation structure while maintaining any settings on the views.

  32. Bill says:

    Holly,
    Thank you for you great blog.

    I have a nagging question about the viewnavigatorapplication. It seem that whenever I am running in the air mode and it loses focus, it closes. This makes for a difficult debugging session. Is there a setting that I can use to have this NOT happen.

    Thanks
    Bill

  33. Chaos7703 says:

    Hey Holly, this is completely off topic, but I didn’t know where else to post it. I’m using Chrome and the comments often “run off” to the right and don’t stay contained in the light gray tan div. I played around with it for a few minutes and adding “width: 97%;” to the .commentList class on line 76 of style-Default.css keeps the comments contained & centered in the comment div. I haven’t tried in any other browsers & perhaps there’s a reason it’s like this. I just thought I’d mention it, in case you didn’t know. Feel free to delete this since it’s unrelated to the topic. =)
    Chaos7703

  34. Stacy says:

    Hi Holly,

    I need help on resolving the issue on desktop air app. Data not committed to the data base until air package is downloaded and tool is restarted. I have to restart the application to save any chnages to the database. Any insight into this would be awesome. I have been stuck with the the issue for a while and I do not find any help on this issue.

    Thank you.
    Stacy

  35. Thomas Wright 'Wizard of Durham' says:

    Excellent overview. Exactly what I needed to know – well, mostly. :)
    Thanks!

Leave a Reply