这是一个由多个部分组成的系列文章的第一部分,它包含了Flex移动开发的若干技巧。如果你过去习惯于桌面和Web编程,你会发现,开发移动应用程序将面临一系列新的挑战。 除了重新思考你的对数据存储和处理的策略,你还需要考虑屏幕尺寸和分辨率,而且需要在管理性能和电池消耗之间做出取舍。本系列文章将通过提出克服这些新的开发挑战的技术来帮助你轻松地过渡到移动开发领域。
数据处理是移动开发涵盖的一个方面,它需要传统的应用程序开发人员采取不同的思维方式。例如,对于许多移动应用程序来说,当用户关闭应用程序或切换到其它应用程序时,或当收到来电时,或当应用程序由于其它一些原因*关闭时,保存数据是非常重要的。 此外,移动应用程序的视图常常被销毁并重新创建。 因此,你需要思考如何处理你的视图之间的数据。因而,你需要用于保存数据的两个场景方案是:
- 在应用程序执行之间(也就是说,在关闭和重启应用程序时)
- 在你的应用程序的视图之间
本文介绍几种处理这些场景的方法。
使用destructionPolicy属性处理视图之间的数据
在深入讨论在视图之间浏览时处理保存数据的不同方法之前,必须了解 View的生命周期。在Flex 4.5中,在任意给定时刻只有一个View是处于激活状态。默认情况下,所有其它的View将予以销毁,并在需要时重新创建。 这种行为是由View类的destructionPolicy
属性控制的,View类的默认设置为auto
。如果你将该属性的值设置为 never
,则View将被缓存,并保持其状态,直到它通过调用 navigator.popView()
被重新调用。
考虑如图1所示的图。默认情况下(或当你明确地设置destructionPolicy="auto"
),当View B推入堆栈时,View A将被销毁,而当View C推入堆栈时,View B将被销毁。 每次当堆栈中View A和 View B之上的视图弹出时,它们将被重新创建。当你在View A和 View B上设置 destructionPolicy="never"
时,这些视图将被去激活和缓存,但当一个新的视图推入时,它们将不会被销毁。之后,当位于它们上面的视图被弹出时,它们会很容易地被激活。
注意即使你在一个View中设置destructionProperty="never"
,如果你调用pushView()
来再次显示它,则它也会被重新创建。只有当上述的View从堆栈中弹出以便显示以前的视图时,缓存才有效。考虑图 2给出的框图。当第二次推入View B 时,它将被重新创建,并且不能访问以前显示的数据(除非你使用另一种方法来保存和检索数据)。
我创建了一款简单的应用程序,它在 View 1启动然后调用pushView()
来激活 View 2。你可以在我在我的应用程序插入的下列跟踪语句中看到默认发生事件的正常顺序(没有设定 destructionPolicy
属性):
- 删除View 1 以响应屏幕变化(
removing
事件) - 将View 2 添加到display列表(
added
事件) - View 2 add 事件(
add
事件) - View 2 创建完成(
creationComplete
事件) - 去激活View 1 (
viewDeactivate
事件) - 从display列表中删除 View 1(
removed
事件) - 激活View 2(
viewActivate
事件)
使用data属性处理视图之间的数据
另一个(在我看来更加直观)用于保存视图之间数据的选项是在View对象中使用 data
属性。这一属性将在内存中保存数据的状态,因此当你通过推入或弹出返回到你的View时,你可以对它进行访问。你可以对data
属性设置任意对象类型,其中包括自定义类。 在下面给出的 Person 类中,data
属性被设置为一个value对象。注意,在View被显示之前,视图中的data
属性已经经过例示并且准备完毕,因此你不必担心其可用性。
你还可以通过在View中覆盖 createReturnObject()
方法从弹出的视图中返回数据。 如果你的应用程序正在显示View A并且你希望推入View B,那么你可以通过在View B中覆盖createReturnObject()
,从View B中将数据返回到View A;例如:
override public function createReturnObject():Object { return person; }
在本范例中, person
是我已经利用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; } }
一旦回到 ViewA之后,你可以使用下面代码访问返回的对象:
data = navigator.poppedViewReturnedObject.object;
由于对象类型实际上是 ViewReturnObject,因此你必须指定object 属性以便获得相应的值。
在应用程序执行之间保存数据
你有若干在应用程序之间保存你的数据的选项,因此当应用程序在关闭之后重新启动时可以获得相应的数据。Flex 4.5 具有一种内置的保存机制,通过 ViewNavigatorApplication或TabbedViewNavigatorApplication中的一个名称为 persistNavigatorState
的属性可以对它进行访问。在你将 persistNavigatorState
设置为true
以便启用该机制之后,你可以侦听navigatorStateLoading
和 navigatorStateSaving
事件,并且在这些事件发生时 执行必要的处理操作。如果你希望开发你自己的自定义保存数据的方法,你可以在事件处理程序中调用preventDefault()
。
当使用这一技巧时,你的根应用程序标签与下面代码相似:
<?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
当你设置persistNavigatorState="true"
时,你的应用程序将自动将其Views的状态和数据保存到一个名称为FXAppCache的本地共享对象中。如果你在Flex 4.5 SDK 中查看PersistenceManager.as 源代码(在我的Mac中,该文件可在文件夹 /Applications/Adobe Flash Builder 4.5/sdks/4.5/frameworks/projects/mobilecomponents/src/spark/managers中找到),你将看到该变量是如何创建的。
在使用 Flash Builder 仿真器运行你的应用程序之后,你可以通过在你的硬盘上寻找相应的本地共享对象查看保存的数据。 在我的Mac中,该文件的路径是:
/Users/hollyschinsky/Library/Preferences/DataPersistence.debug/Local Store/#SharedObjects/DataPersistence.swf/FXAppCache.sol.
如果你需要保存一个自定义类,例如在上面范例中提及的Person类,你必须在主应用程序中侦听preinitialize
事件并且注册该类的别名;例如:
protected function viewnavigatorapplication1_preinitializeHandler(event:FlexEvent):void { flash.net.registerClassAlias("person",model.Person); }
当你将persistNavigatorState
属性设置为true
时,你的数据将自动保存,但如果你希望添加更多属性或访问保存数据管理器(persistence manager)时,你可以通过使用ViewNavigatorApplication或TabbedViewNavigatorApplication的 persistenceManager 对象实现这一目的;例如:
persistenceManager.setProperty("hollysProp","myProperty"); // set my own custom property persistenceManager.getProperty("hollysProp"); // retrieve the value for my property
如果你从一个View访问它,则你可以使用:
FlexGlobals.topLevelApplication.persistenceManager.getProperty("person"); //Retrieves my custom Person class object that was persisted
要想了解内情,你可以打开调试器,然后看一看 persistenceManager
变量即可(参见图4)。
你可以看到在默认情形下还能够保存两个传统的属性:versionNumber
和 navigatorState
,这可以用于将视图回复到它们以前的状态。
使用默认 PersistenceManager的主要缺点是数据不能加密。然而,你可以免费使用 IPersistenceManager 接口来定义一个自定义保存数据的机制。因此,你可能希望实施一种本地加密存储方式以便获取和设置敏感数据。另一个次要缺点是在某些情形下,该机制仅适用于少量数据。如果你需要保存大量数据,你应该使用SQLite替代上述方式。关于一篇仔细分析如何创建能够与SQLite数据库通信的移动应用程序的优秀文章,参见Christophe Coenraets的利用Flex和Flash Builder创建一个移动雇员号码地址簿范例(Building a mobile employee directory sample with Flex and Flash Builder)。