Adobe
产品

首选目的地

  • Creative Suite 6
  • Adobe Marketing Cloud
  • Acrobat
  • Photoshop
  • SiteCatalyst

教育

  • 适用于学生
  • 适用于中小学
  • 适用于高等教育机构

设计和照片

  • Photoshop
  • Illustrator
  • InDesign
  • Lightroom
  • Elements 系列

视频

  • Adobe Premiere
  • After Effects

Web 开发和 HTML5

  • Dreamweaver
  • 游戏 [在新窗口中打开]
  • 移动应用程序

Adobe Marketing Cloud

  • 什么是 Adobe Marketing Cloud?
  • 数字分析
  • Web 体验管理
  • 测试和目标定位

分析

  • SiteCatalyst
  • Adobe Discover
  • Insight

Experience Manager

  • CQ

目标

  • Test&Target
  • Recommendations
  • Search&Promote

媒体优化工具

  • AudienceManager
  • AudienceResearch

文档服务

  • Acrobat
  • SendNow [在新窗口中打开]

出版

  • Digital Publishing Suite

  • 查看所有产品
业务解决方案

按业务需求分

  • 数字分析
  • 数字出版
  • 文档管理
  • 测试和目标定位
  • 视频编缉和服务
  • Web 开发 [在新窗口中打开]
  • Web 体验管理
  • 查看所有业务需求

按行业分

  • 广播
  • 教育
  • 金融服务业
  • 政府部门
  • 出版
  • 零售业
  • 查看所有行业
支持和培训

我需要帮助

  • 产品
  • Adobe Marketing Cloud
  • 论坛 [在新窗口打开]

我想学习

  • 培训和教程
  • 证书 [在新窗口打开]
  • Adobe 开发人员连接
  • Adobe 设计中心
  • Adobe TV [在新窗口打开]
  • Adobe 市场营销中心
  • Adobe Labs [在新窗口中打开]
下载
  • 产品试用
  • Adobe Flash Player
  • Adobe Reader
  • Adobe AIR
  • 查看所有下载
公司
  • Adobe 工作机会
  • 投资者关系
  • 新闻中心
  • 隐私
  • 公司社会责任
  • 客户展示
  • 联系我们
  • 更多公司信息
购买
  • 在线商店
  • 针对学生、教师和职员
  • 批量许可
  • 优惠酬宾
  • 寻找经销商
  • Adobe Marketing Cloud 销售 [在新窗口中打开]
搜索
 
信息 登录
为何登录?登录后可以管理您的帐户,访问试用版下载、产品扩展和社区区域等。
欢迎,
我的 Adobe
我的信息
我的首选项
注销
隐私权 我的 Adobe
Adobe
产品 分类 购买   搜索  
解决方案 公司
帮助 培训
登录 注销 隐私权 我的 Adobe
Date Date
Qty:
Subtotal
Promotions
Estimated Shipping
VAT
Calculated at checkout
Total
Checkout
Adobe 开发者中心 / Flash 开发人员中心 /

Saving state in AIR applications for iOS devices

作者 Ben Garney

Ben Garney
  • CoderHump

Content

  • Considering state issues
  • Saving state on iOS devices
  • Methods for saving
  • State management architecture
  • Where to go from here

Modified

12 May 2011

页面工具

在 LinkedIn 上共享
书签
打印

Tags

要求

必备知识

Familiarity with ActionScript 3 and its object-oriented features as well as familiarity with Flash application development.

用户级别

中级

必需产品

  • Flash Professional CC (Download trial)

A big part of what makes Apple iOS such a great mobile platform is its user interface. Whether you are developing natively or with Adobe Flash Professional CS5.5, users of iOS devices expect you—the application developer—to make your application fit in with the existing iOS experience. An essential part of that experience is interruptibility: your Adobe AIR application needs to save its state frequently and automatically so that it can be switched automatically as users expect—and as Apple specifies in its iPhone Human Interface Guidelines.

This article presents some strategies for saving your application state on iOS devices. The basic capabilities of iOS devices are covered, as well as application design techniques to help give a seamless user experience. I also provide code that demonstrates different techniques for saving state.

Considering state issues

The Apple iOS runs only a single user application at a time to keep the device responsive. Whenever the user launches another application, or an outside event like a phone call occurs, your application can be terminated. This can lead to frustrations for the user: imagine if you were writing a text message or e-mail and a phone call came in and you lost your work! Because of this, it is essential that your application save its state regularly and automatically.

My colleagues and I at PushButton Labs developed a game called Trading Stuff in Outer Space for the iPhone last year using the Flash Professional CS5 (see Figure 1). It is a simple space-based trading game that was developed in eight days to explore the technology. One of the goals of the project was to provide an experience indistinguishable from any other iPhone game. As a result, we had to figure out how to make saving state work.

Trading Stuff in Outer Space game screen
Figure 1. Trading Stuff in Outer Space game screen

Saving state on iOS devices

We envisioned the scenario of applications that lose data when they are interrupted. Of course, the built-in iOS apps don't do that—when you go back to your text messages after that interrupting phone call, you pick up right where you left off. This is done by the simple expedient of saving state when the application is ordered to quit by the OS. In the Objective-C world, you will have to implement this yourself (see the appropriate section of the iOS Application Programming Guide), and it is no different when you are developing with Flash.

Saving on exit

How do you know when to save in Flash? Since AIR applications for iOS share the same API as AIR apps for Adobe AIR, you can listen for the NativeApplication.EXITING event to know when to save your state:

package { import flash.desktop.NativeApplication; import flash.display.Sprite; import flash.events.Event; public class Save1 extends Sprite { public function Save1() { // Listen for exiting event. NativeApplication.nativeApplication.addEventListener(Event.EXITING, onExit); // Load data. load(); } private function onExit(e:Event):void { trace("Save here."); } private function load():void { trace("Load here."); } } }

Saving on important events

However, you should also save after crucial events or after a certain interval has passed. For Trading Stuff in Outer Space, crucial events include whenever the player trades (since it is an action that takes a lot of thought and has major effects on gameplay), when the player arrives at a planet, and whenever the player starts or ends a game. For an interval, we chose a period of 30–60 seconds, so that the user would never lose more than a minute's playtime. We chose that threshold because we felt that it was high enough to allow responsive play but low enough to avoid infuriating a user if something causes saving-on-exit to fail.

Unfortunately, the EXITING event isn't 100% reliable. It doesn't always get fired—sometimes due to time limits in the OS, sometimes due to other causes. Your application may crash (although it's unlikely), in which case the normal exiting behavior won't happen. So what we did was to save after every major operation the user performed, as well as approximately once a minute. That way even if users did manage to quit without the application saving state, it was unlikely they would lose more than few seconds of work:

package { import flash.desktop.NativeApplication; import flash.display.Sprite; import flash.events.Event; import flash.utils.setInterval; public class Save2 extends Sprite { public function Save2() { // Listen for exiting event. NativeApplication.nativeApplication.addEventListener(Event.EXITING, onExit); // Also save every 30 seconds. setInterval(save, 30*1000); // Load data. load(); } private function onExit(e:Event):void { save(); } private function save():void { trace("Save here."); } private function load():void { trace("Load here."); } } }

General applicability

The issues facing an AIR app on iOS devices are not that different from those facing SWF content in the browser. Users' browsers might crash or users might (accidentally or on purpose) navigate away from the page. Even desktop users may want their applications to always be stateful. You can use the same saving code to enhance the user experience on the web, devices, and the desktop.

Methods for saving

You can save your state in two main ways with AIR applications on iOS devices. One is with an LSO (local SharedObject). As you know, SharedObject.getLocal() (see the documentation) can be used to store data locally. This is convenient for a variety of reasons, not least of which is that you can use AMF to store object graphs:

private function save():void { // Get the shared object. var so:SharedObject = SharedObject.getLocal("myApp"); // Update the age variable. so.data['age'] = int(so.data['age']) + 1; // And flush our changes. so.flush(); // Also, indicate the value for debugging. trace("Saved generation " + so.data['age']); } private function load():void { // Get the shared object. var so:SharedObject = SharedObject.getLocal("myApp"); // And indicate the value for debugging. trace("Loaded generation " + so.data['age']); }

In the end, serializing objects directly worked against us in this game application. Depending on your application, using SharedObject might be a perfect fit. For Trading Stuff, I had a lot of complex interrelated data to store, and I also wanted to segregate game state into frequently changed and infrequently changed elements, so using LSOs wasn't a good fit. More on that next.

Saving using file objects

The other approach is to use File objects directly. You can write asynchronously in order to avoid disrupting the frame rate. If different parts of the game state change at different frequencies, you can store them in separate files so that only what actually changes is touched. You have to do your own serialization, but this isn't as bad as it sounds. (You can even use readObject() and writeObject() to store a whole object at once, although this can cause problems in some cases.)

public var age:int = 0; /** * Get a FileStream for reading or writing the save file. * @param write If true, we will write to the file. If false, we will read. * @param sync If true, we do synchronous writes. If false, asynchronous. * @return A FileStream instance we can read or write with. Don't forget to close it! */ private function getSaveStream(write:Boolean, sync:Boolean = true):FileStream { // The data file lives in the app storage directory, per iPhone guidelines. var f:File = File.applicationStorageDirectory.resolvePath("myApp.dat"); if(f.exists == false) return null; // Try creating and opening the stream. var fs:FileStream = new FileStream(); try { // If we are writing asynchronously, openAsync. if(write && !sync) fs.openAsync(f, FileMode.WRITE); else { // For synchronous write, or all reads, open synchronously. fs.open(f, write ? FileMode.WRITE : FileMode.READ); } } catch(e:Error) { // On error, simply return null. return null; } return fs; } private function load():void { // Get the stream and read from it. var fs:FileStream = getSaveStream(false); if(fs) { try { age = fs.readInt(); fs.close(); } catch(e:Error) { trace("Couldn't load due to error: " + e.toString()); } } trace("Loaded age = " + age); } private function save():void { // Update age. age++; // Get stream and write to it – asynchronously, to avoid hitching. var fs:FileStream = getSaveStream(true, false); fs.writeInt(age); fs.close(); trace("Saved age = " + age); }

Both of these should be straightforward to understand. If you've done Flash or AIR development, you've almost certainly dealt with either SharedObject or File. If not, you might want to refer to their documentation for details and examples.

You can also use readObject() and writeObject() to store or retrieve an object from a File object. This is powerful and saves you from a lot of repetitive serialization code. However, if you aren't careful, it can introduce problems: for instance, saving a DisplayObject won't work due to the various helper objects and complex relationships involved. Even saving pure data classes can be risky because they may have references to other objects that you don't want to serialize.

Saving the state of your whole application is, of course, more involved. I discuss this issue next.

State management architecture

The larger issue I ran into was serializing all my application data. Figuring out where to write is easy, but figuring out what to write is a little trickier. Naturally, because I am a programmer, I wanted to do it with as little work as possible.

The first thing I tried was directly serializing parts of my DisplayObject hierarchy and game state via AMF in a SharedObject. This was attractive initially because I figured I could throw references to a few key parts of my application into the SharedObject and be done. However, this didn't work: DisplayObjects have lots of helper objects with strange construction restrictions that are not AMF-friendly. It also led to uncontrolled serialization, where the object I was serializing contained a reference to other objects that ended up bringing in most of my application—and I didn't want to waste time debugging issues from strange dangling objects being serialized.

So, instead, I switched to use the File save() method. This meant I had to write some code to load and save objects that made up the state of my application. Up front, this involved more lines of code, but since they were direct and simple—and I did not plan on modifying the application much past launch—this approach ended up being much quicker to write and debug than the fewer lines of code that would be required for a more automatic solution.

The following code snippet shows what saving and loading looks like for the user's current waypoint:

// Serialize current waypoint. gfs.writeInt(currentWaypointIndex); gfs.writeBoolean(wayPoint != null); if(wayPoint != null) { gfs.writeFloat(wayPoint.x); gfs.writeFloat(wayPoint.y); } // Load waypoint state. currentWaypointIndex = gfs.readInt(); if(gfs.readBoolean()) { wayPoint = new Point(); wayPoint.x = gfs.readFloat(); wayPoint.y = gfs.readFloat(); } else { wayPoint = null }

As you can see, the serialization code has to allocate objects when appropriate; it has to detect if variables are null, and note that in the file; and it has to write each field with the right type. This can be a little overwhelming at first, but with a little practice you'll quickly realize that you are reusing a small set of common idioms for nearly every task.

I also made a singleton to manage all my game state. It kept track of the player's inventory, the locations of everything in the universe, the status of enemies, progress with missions, and so forth. From this singleton, I had methods to reset the game's state, to write it, and to read it back again. In some cases (such as inventory or missions) the state was owned by the singleton. In other cases (such as positions of game objects like the player and planets) the state is managed elsewhere, by other objects, but the singleton can get to it for saving purposes. On top of this foundation, it was easy to implement various saving strategies, as mentioned previously.

Where to go from here

Saving state is a key part of making your content fit smoothly into the iOS experience. Since only one application at a time can currently run on iOS devices, it's important to store not only the user's data, but the state of your application's UI. It's not a difficult detail, but it is an important one. I hope this article has helped you understand the key pieces involved in implementing this important functionality in your own application.

Saving state is a broad topic; there's no one-size-fits-all solution. A quick overview of the many different options for serialization can be found in the Serialization entry on Wikipedia. Flash natively supports XML (see Colin Moock's book, Essential ActionScript 3, for several great chapters on XML and E4X) and AMF, which are good building blocks for your serialization needs. There are also libraries like as3corelib that add support for JSON and other serialization formats. Of course, when you're saving application state between runs, what matters most is that the technology you choose is simple, easy to work with, and reliable.

From here, you might want to take the sample code included with this article and try to integrate it with your own content. There are a few decision points, such as using local SharedObjects or File objects, or saving frequently vs. infrequently. Make sure to run a few tests before deciding one way or the other; informed decisions are always best, and the information you need will depend on your specific application. Once you have a solution, test it by frequently quitting in the middle of your program's execution. It might not be possible to restore every piece of state exactly, but with surprisingly little work you will get close enough so that users accept your application as part of their positive iOS device experience.

Check out this series of articles to help you learn more about developing for iOS using Flash Professional:

  • Developing for iOS using Flash
  • Using screen orientation APIs for smartphone application development
  • Optimizing content for Apple iOS devices
  • Guide for Apple App Store submissions

Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License

More Like This

  • Introducing the ActionScript 3.0 debugger
  • Flash glossary: Sprite
  • Flash glossary: Instance
  • Flash glossary: Variable
  • Flash glossary: Components
  • Five demos of Flash CS4 Professional
  • Understanding ActionScript 3 debugging in Flash
  • Flash glossary: Actions panel
  • Flash glossary: Frame label
  • Flash glossary: Shared object

产品

  • Creative Suite 6
  • Adobe Marketing Cloud
  • Acrobat
  • Photoshop
  • Digital Publishing Suite
  • Elements 系列
  • SiteCatalyst

下载

  • 产品试用
  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR

支持和培训

  • 产品帮助
  • 论坛

购买

  • 在线商店
  • 针对学生、教师和职员
  • 批量许可
  • 优惠酬宾
  • 寻找经销商

公司

  • 新闻中心
  • 合作伙伴计划
  • 公司社会责任
  • 工作机会
  • 投资者关系
  • 事件
  • 法律
  • 安全性
  • 联系 Adobe
选择您的地区 中国(更改)
选择您的地区 关闭

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2013 Adobe Systems Software Ireland Ltd. All rights reserved.

使用条款 | 隐私权 (已更新) | Cookies

京 ICP 备 10217899 号 京公网安备 110105010404