对于应用程序的每一个运行时状态,系统有不同的期望,而你的应用程序处于那个状态。当状态转换发生时,系统会通知app对象,然后通知它的app委托。您可以使用UIApplicationDelegate协议的状态转换方法来检测这些状态更改并作出适当的响应。例如,当从前台转换到后台时,您可能会写出任何未保存的数据并停止任何正在进行的任务。以下部分提供了如何实现状态转换代码的技巧和指导。
在启动时做什么
当你的应用程序启动时(不管是在前台还是后台),使用你的应用delegate的application:willFinishLaunchingWithOptions:以及application:didFinishLaunchingWithOptions:方法:
-
查看启动选项字典的内容,了解应用程序为什么启动,并作出适当的响应。
-
初始化应用程序的关键数据结构。
-
准备应用程序的窗口和视图,以便显示:
-
使用OpenGL ES绘图的应用程序不能使用这些方法来准备绘图环境。相反,将任何绘制OpenGL的调用推迟到[applicationDidBecomeActive:]方法。
-
由application:willFinishLaunchingWithOptions:方法显示你的应用程序窗口。UIKit延迟使窗口可见,直到application:didFinishLaunchingWithOptions:方法返回。
-
在启动时,系统会自动加载您的应用程序的主故事板文件并加载初始的视图控制器。对于支持状态恢复的应用程序,状态恢复机制会在调用应application:willFinishLaunchingWithOptions:和application:didFinishLaunchingWithOptions: methods之间将界面恢复到以前的状态。使用application:willFinishLaunchingWithOptions:显示应用程序窗口的方法,并确定是否应该进行状态恢复。使用application:didFinishLaunchingWithOptions:方法对应用程序的用户界面进行任何最终调整。
application:willFinishLaunchingWithOptions:和application:didFinishLaunchingWithOptions:方法应该尽可能地轻量级,以减少应用程序的启动时间。应用程序将在5秒内启动、初始化并开始处理事件。如果一个应用程序没有及时完成它的发布周期,系统就会因为它没有响应而杀死它。因此,任何可能降低启动速度的任务(例如访问网络)都应该在辅助线程上执行。
启动周期
当您的应用程序启动时,它将从非运行状态移动到活动状态或后台状态,在非活动状态中短暂过渡。作为启动周期的一部分,系统为你的应用创建一个进程和主线程,并在主线程上调用你的应用的主功能。Xcode项目附带的默认主函数立即将控制权交给UIKit框架,该框架负责初始化应用程序并使其运行。
图4-1显示了应用程序启动到前台时发生的事件序列,包括调用的应用程序委托方法。
图4-1当你的应用程序启动到后台时——通常是为了处理某种类型的后台事件——启动周期与图4-2所示的稍有不同。主要的区别是,你的应用程序不是被激活的,而是进入后台状态来处理事件,然后在某个时间点被暂停。当进入后台时,系统仍然会加载你的应用程序的用户界面文件,但它不会显示应用程序的窗口。
图4-2要确定应用程序是否启动到前台或后台,在application:willFinishLaunchingWithOptions:或application:didFinishLaunchingWithOptions: delegate方法中检查共享application对象的applicationState属性。当应用程序启动到前台时,此属性包含UIApplicationStateInactive值。当应用程序启动到后台时,属性包含值UIApplicationStateBackground。您可以使用这种差异来相应地调整委托方法的启动时行为。
注意: 当应用程序启动以打开URL时,启动事件的顺序与图4-1和图4-2所示略有不同。有关打开URL时发生的启动序列的信息,请参见 see Handling URL Requests.
横屏启动
应用程序只使用景观方向的界面,必须明确地要求系统在这个方向上启动应用程序。通常,应用程序以纵向模式启动,并根据需要旋转界面以匹配设备的方向。对于同时支持纵向和横向方向的应用程序,始终为纵向模式配置视图,然后让视图控制器处理任何旋转。但是,如果你的应用程序支持横向而不支持纵向方向,则执行以下任务,使它最初以横向模式启动:
- 添加UIInterfaceOrientation 键到你的应用的info.plist文件中。将此键的值设置为UIInterfaceOrientationLandscapeLeft 或 UIInterfaceOrientationLandscapeRight。
- 在横向模式下显示视图,并确保它们的布局或自动调整选项设置正确。
- 重写视图控制器的shouldAutorotateToInterfaceOrientation:方法,横向设置返回YES,纵向设置返回NO。
重要提示:应用程序应该始终使用视图控制器来管理基于窗口的内容。
info.plist文件中的UIInterfaceOrientation键告诉iOS,它应该配置app状态栏的方向(如果显示了),以及任何视图控制器在启动时管理的视图的方向。视图控制器尊重这个键并设置它们的视图的初始方向以匹配。使用此键相当于在您的applicationDidFinishLaunching:方法的执行早期调用setStatusBarOrientation:animated: UIApplication的方法。
在首次启动时安装特定于应用程序的数据文件
您可以使用应用程序的第一个启动周期来设置运行所需的任何数据或配置文件。特定于应用程序的数据文件应该在 Library/Application Support/<bundleID>/ directory of your app sandbox,其中bundleID是应用程序的包标识符。您可以进一步细分此目录,以便根据需要组织数据文件。您还可以在其他目录中创建文件,比如应用程序的iCloud容器目录或本地文档目录,这取决于您的需求。
如果您的应用程序包包含您计划修改的数据文件,请将这些文件从应用程序包中复制并修改副本。您不能修改应用程序包中的任何文件。因为iOS应用程序是有代码签名的,所以修改应用程序包中的文件会使应用程序签名失效,并将阻止应用程序在未来启动。将这些文件复制到应用程序支持目录(或沙箱中的另一个可写目录)并修改它们是安全使用这些文件的唯一方法。
当你的应用程序被临时中断时该怎么办
基于警告的中断会导致你的应用程序暂时失去控制。你的应用程序继续在前台运行,但它不会接收来自系统的触摸事件。(不过,它确实继续接收通知和其他类型的事件,比如加速度计事件。)为了响应这一变化,您的应用程序应该在其applicationWillResignActive:方法中执行以下操作:
- 保存数据和任何相关的状态信息。
- 停止计时器和其他周期性任务。
- 停止任何正在运行的元数据查询。
- 不要开始任何新的任务。
- 暂停电影回放(播放回放时除外)。
- 如果你的应用是一款游戏,请进入暂停状态。
- Throttle back OpenGL ES frame rates.
- 暂停执行非关键代码的任何调度队列或操作队列。(您可以在不活动时继续处理网络请求和其他时间敏感的后台任务。)
当您的应用程序被移回活动状态时,它的 [applicationDidBecomeActive:]
方法应该会反转在applicationWillResignActive:方法中所采取的任何步骤。因此,在重新激活时,应用程序应该重新启动计时器、恢复调度队列,并再次调高OpenGL ES帧速率。然而,游戏不应该自动恢复;它们应该保持暂停状态,直到用户选择继续使用它们。
当用户按下Sleep/Wake按钮时,带有NSFileProtectionComplete保护选项保护文件的应用程序必须关闭对这些文件的任何引用。对于配置了适当密码的设备,按下Sleep/Wake按钮会锁住屏幕,并迫使系统丢弃启用了完整保护的文件的解密密钥。当屏幕被锁定时,任何访问相应文件的尝试都将失败。因此,如果您有这样的文件,您应该关闭应用程序中对它们的任何引用。
重要:在你的应用程序中,在适当的检查点保存用户数据。虽然你可以使用app状态转换来强制对象将未保存的更改写入磁盘,但不要等待应用程序状态转换来保存数据。例如,管理用户数据的视图控制器在被删除时应该保存数据。
应对暂时中断
当一个基于警报的中断发生时,比如来电,应用程序会临时移动到不活跃的状态,这样系统就可以提示用户如何进行。应用程序保持这种状态,直到用户取消警报。此时,应用程序要么返回到活动状态,要么移动到后台状态。图4-3显示了当基于警报的中断发生时,通过应用程序的事件流。
图4-3显示横幅的通知不会像基于警报的通知那样使你的应用失效。相反,横幅被放置在你的应用窗口的顶部边缘,你的应用继续像以前一样接收触摸事件。但是,如果用户拉下横幅来显示通知中心,您的应用程序就会移动到非活动状态,就好像发生了基于警报的中断。在用户取消通知中心或启动另一个应用程序之前,您的应用程序仍然处于非活动状态。此时,您的应用程序将移动到适当的活动状态或后台状态。用户可以使用Settings应用程序来配置显示横幅和警告的通知。
按下Sleep/Wake按钮是另一种中断类型,会导致应用程序暂时停用。当用户按下这个按钮时,系统会禁用触摸事件,将应用程序移动到后台,将应用程序的applicationState属性的值设置为UIApplicationStateBackground,并锁定屏幕。对于使用数据保护来加密文件的应用程序来说,锁定的屏幕会带来额外的后果。当你的应用程序被临时中断时,这些后果将会被描述出来。
当你的应用程序进入前台时该怎么做
回到前台是你的应用程序重新启动任务的机会,当它移动到后台时停止。在移动到前台时发生的步骤如图4-4所示。applicationWillEnterForeground:方法应该撤销在您的applicationDidEnterBackground:方法中所做的任何事情,而applicationDidBecomeActive:方法应该继续执行与启动时相同的激活任务。
图4-4注意:当您的应用程序重新进入前台时,UIApplicationWillEnterForegroundNotification 通知也可以用于跟踪。应用程序中的对象可以使用默认的通知中心注册此通知。
准备好处理队列通知
当它返回到前台或后台执行状态时,处于挂起状态的应用程序必须准备好处理任何队列通知。被挂起的应用程序不执行任何代码,因此无法处理与方向变化、时间变化、首选项变化以及其他可能影响应用程序外观或状态的通知。为了确保这些更改不会丢失,系统对许多相关通知进行排队,并在应用程序再次执行代码时(无论是在前台还是后台)将它们发送到应用程序。为了防止您的应用程序在重新启动时出现通知过载,系统合并事件并提供单个通知(每种相关类型),反映应用程序暂停后的净更改。
表4-1列出了可以合并并交付给应用程序的通知,其中大多数通知都直接发送给注册的观察者。一些,比如那些与设备朝向改变有关的,通常会被系统框架拦截,然后以另一种方式交付给你的应用。
Event | Notifications |
---|---|
配件连接或断开 | EAAccessoryDidConnectNotification EAAccessoryDidDisconnectNotification |
设备方向变化 | UIDeviceOrientationDidChangeNotification 除了这个通知,视图控制器会自动更新它们的接口方向。 |
有重大的时间变化 | UIApplicationSignificantTimeChangeNotification |
电池电量或电池状态改变 | UIDeviceBatteryLevelDidChangeNotification UIDeviceBatteryStateDidChangeNotification |
距离情况改变 | UIDeviceProximityStateDidChangeNotification |
受保护文件的状态改变 | UIApplicationProtectedDataWillBecomeUnavailable UIApplicationProtectedDataDidBecomeAvailable |
外部显示被连接或断开 | UIScreenDidConnectNotification UIScreenDidDisconnectNotification |
显示的屏幕模式改变 | UIScreenModeDidChangeNotification |
你的应用通过设置应用公开的首选项改变了 | NSUserDefaultsDidChangeNotification |
当前语言或语言环境设置已更改 | NSCurrentLocaleDidChangeNotification |
用户的iCloud账户状态发生变化。 | NSUbiquityIdentityDidChangeNotification |
在您的应用程序的主运行循环中传递队列通知,通常在任何触摸事件或其他用户输入之前交付。大多数应用程序应该能够足够快地处理这些事件,以便在恢复时不会造成任何明显的延迟。但是,如果您的应用程序在从后台状态返回时显得迟缓,使用工具来确定您的通知处理程序代码是否导致了延迟。
返回前台的应用程序还会接收到自上次更新以来标记为脏的任何视图的视图更新通知。在后台运行的应用程序仍然可以调用setNeedsDisplay或setNeedsDisplayInRect:方法来请求视图的更新。但是,由于视图不可见,系统只有在应用程序返回前台后才合并请求并更新视图。
处理iCloud的变化
如果iCloud状态因任何原因发生变化,系统会向你的app发送一个NSUbiquityIdentityDidChangeNotification 通知。此通知是应用程序更新缓存和任何与icloud相关的用户界面元素以适应更改的提示。例如,当用户退出iCloud时,您应该删除对所有基于iCloud的文件或数据的引用。
如果您的应用程序已经提示用户是否在iCloud中存储文件,那么当iCloud状态发生变化时,不要再次提示。在第一次提示用户之后,将用户的选择存储在应用程序的本地首选项中。然后,您可能想要使用Settings bundle或作为应用程序中的选项来公开该首选项。但是,除非该首选项当前不在用户默认数据库中,否则不要重复该提示符。
处理语言环境的变化
如果用户在应用程序挂起时更改了当前语言环境,您可以使用NSCurrentLocaleDidChangeNotification 通知强制更新任何包含对语言环境敏感信息的视图,比如当应用程序返回前台时的日期、时间和数字。当然,避免与语言环境相关的问题的最好方法是编写易于更新视图的代码。例如:
-
在检索NSLocale对象时,使用[autoupdatingCurrentLocale]类方法。此方法返回一个locale对象,该对象会根据更改自动更新自身,因此您永远不需要重新创建它。但是,当语言环境发生变化时,仍然需要刷新包含来自当前语言环境的内容的视图。
-
当当前语言环境信息发生变化时,重新创建任何缓存的日期和数字格式化器对象
处理设置的更改
如果你的应用有由设置应用管理的设置,它应该遵守NSUserDefaultsDidChangeNotification通知。因为用户可以在应用程序挂起或在后台修改设置,所以您可以使用此通知来响应这些设置中的任何重要更改。在某些情况下,响应这个通知可以帮助关闭一个潜在的安全漏洞。例如,电子邮件程序应该响应用户帐户信息的更改。未能监控这些更改可能会导致隐私或安全问题。具体来说,当前用户可能可以使用旧的帐户信息发送电子邮件,即使该帐户不再属于该用户。
在收到NSUserDefaultsDidChangeNotification通知后,您的应用程序应该重新装载任何相关的设置,如果需要,还应该适当地重新设置它的用户界面。在密码或其他与安全相关的信息发生变化的情况下,还应该隐藏以前显示的信息,并强制用户输入新的密码。
当你的应用进入后台时该怎么办
当从前台执行到后台执行时,使用app委托的applicationDidEnterBackground:方法执行以下操作:
-
准备好让你的应用拍照。
当你的applicationDidEnterBackground:方法返回时,系统会获取你的应用程序用户界面的图片,并使用生成的图片进行转换动画。如果接口中的任何视图包含敏感信息,您应该在applicationDidEnterBackground:方法返回之前隐藏或修改这些视图。如果在此过程中向视图层次结构中添加新视图,则必须强制这些视图自己绘制,如在为应用程序快照准备时所述。 -
保存任何相关的app状态信息。
在进入背景之前,你的应用程序应该已经保存了所有重要的用户数据。使用到后台的转换来保存对应用程序状态的最后一分钟更改。 -
根据需要释放内存。
释放任何你不需要的缓存数据,并做一些简单的清理工作,以减少应用程序的内存占用。内存占用较大的应用程序是第一个被系统终止的,所以释放图像资源、数据缓存和其他不再需要的对象。有关更多信息,请参见 减少内存占用.
你的应用程序委托的应用程序后台:方法有大约5秒的时间来完成任何任务和返回。在实践中,这种方法应该尽可能快地返回。如果该方法在时间耗尽之前没有返回,那么您的应用程序将被杀死并从内存中清除。如果您仍然需要更多的时间来执行任务,可以调用beginbacktaskwithexpirationhandler:方法来请求后台执行时间,然后在辅助线程中启动任何长时间运行的任务。无论您是否启动任何后台任务,applicationDidEnterBackground:方法必须在5秒内退出。
注意:除了调用applicationDidEnterBackground:方法外,系统还发送UIApplicationDidEnterBackgroundNotification 通知。您可以使用该通知将清理任务分发给应用程序的其他对象。
根据你的应用程序的特性,当你移动到后台时,你的应用程序还应该做一些其他的事情。例如,任何活动的Bonjour服务都应该暂停,应用程序应该停止调用OpenGL ES函数。
后台过渡周期
当用户按下Home按钮,按下Sleep/Wake按钮,或者系统启动另一个应用程序时,前台应用程序切换到非活动状态,然后切换到后台状态。这些转换导致调用应用程序委托的applicationWillResignActive:和applicationDidEnterBackground:方法,如图4-5所示。在从applicationDidEnterBackground:方法返回后,大多数应用程序随后很快就会进入暂停状态。请求特定后台任务(如播放音乐)或请求系统额外执行时间的应用程序可能会持续运行一段时间。
图4-5准备应用程序快照
在应用委托的applicationDidEnterBackground: method返回后不久,系统就会对应用程序的windows进行快照。类似地,当应用程序被唤醒来执行后台任务时,系统可能会获取一个新的快照来反映任何相关的更改。例如,当应用程序被唤醒来处理下载的项目时,系统将获取一个新的快照,以便反映项目合并所引起的任何更改。系统在多任务UI中使用这些快照图像来显示应用程序的状态。
如果您在进入后台时对视图进行更改,您可以调用snapshotViewAfterScreenUpdates:主视图的方法,以强制执行这些更改。在视图上调用setNeedsDisplay方法对快照无效,因为快照是在下一个绘制周期之前获取的,因此不会呈现任何更改。调用snapshotViewAfterScreenUpdates:值为YES的方法强制立即更新快照机制使用的底层缓冲区。
减少内存占用
每个应用程序都应该在进入后台时释放尽可能多的内存。系统试图尽可能同时在内存中保存尽可能多的应用程序,但当内存不足时,它会终止挂起的应用程序以收回内存。在后台使用大量内存的应用程序是第一个被终止的应用程序。
实际上,只要不再需要对象,应用程序就应该删除对对象的强引用。删除强引用使编译器能够立即释放对象,以便回收相应的内存。但是,如果您想缓存一些对象以提高性能,您可以等到应用程序转换到后台时再删除对它们的引用。
您应该尽快删除强引用的对象的一些示例包括:
-
图像对象创建。(UIImage的一些方法返回图像,其底层图像数据被系统自动清除。有关更多信息,请参见 UIImage Class Reference. )
-
可以从磁盘重新加载的大型媒体或数据文件
-
您的应用程序不需要的任何其他对象,并且以后可以轻松地重新创建
为了减少应用程序的内存占用,当应用程序移动到后台时,系统会自动清除为应用程序分配的一些数据。
-
系统清除所有核心动画层的后台存储。这一努力不会从内存中删除应用程序的层对象,也不会改变当前层的属性。它只是防止这些层的内容出现在屏幕上,这是由于应用程序是在后台,所以无论如何也不应该发生。
-
它删除对缓存映像的任何系统引用。
-
它删除对其他系统管理数据缓存的强引用。
网友评论