Rabu, 07 September 2011

Data handling Flex mobile development tips


This is Part 1 of a multipart series of articles that cover tips and tricks for Flex mobile development. If you are used to desktop and web programming, you'll find that developing mobile applications presents a new set of challenges. In addition to rethinking your strategies for data persistence and handling, you'll need to take into account screen size and resolution and make trade-offs to manage performance and battery drain. This series of articles will help ease the transition into mobile development by presenting techniques for overcoming these new development challenges.

Handling data is one aspect of mobile development that requires traditional application developers to adopt a different way of thinking. For instance, for many mobile applications it is important to persist (store) data when the user closes or switches away from the application, when an incoming call is received, or when the application is forced to shut down for some other reason. In addition, mobile application Views are destroyed and recreated often. As a result you'll need to think about how to handle your data between views. So, the two main scenarios in which you need to save data are:

Between application executions (that is, when closing and restarting the application)
Between views within your application
This article covers several ways of handling these scenarios.

Using the destructionPolicy attribute to handle data between views

Before diving into the different ways to handle saving data while navigating between views, 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. This behavior is controlled by the destructionPolicy property on the View class, which is set to auto by default. If you set the value of this property to never , the View will be cached and hold its state until it is reactivated via a call to navigator.popView() .

Consider the diagram shown in Figure 1. By default (or when you explicitly set destructionPolicy="auto" ), View A will be destroyed when View B is pushed onto the stack, and View B will be destroyed when View C is pushed onto the stack. View A and View B will be recreated each time the view above it in the stack is popped off. When you set destructionPolicy="never" on View A and View B, those views will be deactivated and cached, but not destroyed, when a new view is pushed. Later, when the view above them is popped, they will simply be activated.
Note that even if you set destructionProperty="never" on a View, it will still be recreated if you invoke pushView() to show it again. The caching only works when the View above is popped off the stack to reveal a previous view. Consider the diagram in Figure 2. When View B is pushed for the second time, it will be recreated and no data from the previous showing will be accessible (unless you use another method to persist and retrieve the data).

I created a simple application that starts in View 1 and invokes pushView() to activate View 2. The normal order of events that occur by default (without setting the destructionPolicy property) can be seen in the following trace statements that I inserted in my application:

View 1 being removed in response to screen change ( removing event)
View 2 added to display list ( added event)
View 2 add event ( add event)
View 2 creation complete ( creationComplete event)
View 1 deactivated ( viewDeactivate event)
View 1 is being removed from display list ( removed event)
View 2 activated ( viewActivate event)
Using the data property to handle data between views

Another (and in my opinion more intuitive) option for saving data between views is to use the data property on the View object. This property will save the state of the data in memory so when you return to your View via a push or pop it can be accessed. You can set any type of object on the data property, including a custom class. In the Person class example shown below, the data property is set to a value object. 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. If your app is showing View A and you push View B, you can then return data from View B to View A by overriding createReturnObject() in View B; for example:

override public function createReturnObject():Object
{
    return person;
}
In this example, person is an instance of a class I have defined in ActionScript:

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;
    }
}
Once back in View A, you can access the returned object using:

data = navigator.poppedViewReturnedObject.object;
Since the object type is actually ViewReturnObject, you have to specify the object property to get the value.

Persisting data between application executions

You have several options for persisting your data between application executions, so that the data is available when the application is restarted after it has been shut down. Flex 4.5 has a built-in persistence mechanism that is accessed via a property named persistNavigatorState on ViewNavigatorApplication or TabbedViewNavigatorApplication. After you set persistNavigatorState to true to enable this mechanism, you can listen for the navigatorStateLoading and navigatorStateSaving events, and perform any handling needed when those events occur. If you want to develop your own custom persistence method, you can call preventDefault() in the events handler.

When using this technique, your root application tag would look something like the following:

<?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)" >
/Applications/Adobe Flash Builder 4.5/sdks/4.5/frameworks/projects/mobilecomponents/src/spark/managers
When you set persistNavigatorState="true" , your application will automatically save the state of its Views and data to a local shared object with the name FXAppCache. If you look at the PersistenceManager.as source code from the Flex 4.5 SDK (on my Mac this file is found under /Applications/Adobe Flash Builder 4.5/sdks/4.5/frameworks/projects/mobilecomponents/src/spark/managers), you will see how this variable is created:

After running your application in the Flash Builder emulator you can look at what was saved by locating the local shared object on your hard drive. On my Mac, the path to the file is:

/Users/hollyschinsky/Library/Preferences/DataPersistence.debug/Local Store/#SharedObjects/DataPersistence.swf/FXAppCache.sol.

Figure 3. Persisted data in the FXAppCache local shared object.
If you need to persist a custom class, such as the Person class in the example above, you'll have to listen for the preinitialize event in the main application and register the class alias; for example:

protected function viewnavigatorapplication1_preinitializeHandler(event:FlexEvent):void
{
    flash.net.registerClassAlias("person",model.Person);
}
Your data will automatically persist when you simply set the persistNavigatorState property to true , 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 object; for example:

persistenceManager.setProperty("hollysProp","myProperty"); // set my own custom property
persistenceManager.getProperty("hollysProp"); // retrieve the value for my property
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
To see what's going on behind the scenes, it's helpful to open the debugger and take a look at the persistenceManager variable (see Figure 4).


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

The main disadvantage with using the default PersistenceManager is that the data is not encrypted. However, you are free to define a custom persistence mechanism using the IPersistenceManager interface. So you might, for example, want to implement an encrypted local store for getting and setting sensitive data. Another minor disadvantage, in some cases, is that this mechanism is intended for small amounts of data. If you have a great deal of data to be saved you would want to use SQLite instead. For a great article that walks through creating a mobile application that writes to a SQLite database, see  Building a mobile employee directory sample with Flex and Flash Builder  by Christophe Coenraets.


Tidak ada komentar:

Posting Komentar