APP编程指南 (四) —— 处理应用程序状态转换的策略(一)

版本记录

版本号 时间
V1.0 2018.05.31

前言

我们在做一个APP时候需要注意哪些方面呢,接下来我们就看一下APP编程指南。里面有些可能大家在平时编程中都经历过,但是再系统的了解下也不是坏事。感兴趣的可以看上面写的几篇。
1. APP编程指南 (一) —— 基本概览(一)
2. APP编程指南 (二) —— 应用程序必须实现的行为(一)
3. APP编程指南 (三) —— 应用程序的后台执行(一)

Strategies for Handling App State Transitions - 处理应用程序状态转换的策略

对于应用程序的每种可能的运行时状态,当您的应用程序处于该状态时,系统会有不同的期望。 当状态转换发生时,系统会通知应用程序对象,然后通知应用程序代理。 您可以使用UIApplicationDelegate协议的状态转换方法来检测这些状态更改并作出适当的响应。 例如,当从前台转换到后台时,您可以写出任何未保存的数据并停止正在进行的任务。 以下各节提供了有关如何实现状态转换代码的提示和指导。


What to Do at Launch Time - 启动时要做什么

当您的应用程序启动时(无论是前台还是后台),请使用您的应用程序委托application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:方法执行以下操作:

  • 检查启动选项字典的内容,了解启动应用程序的原因,并做出适当的响应。
  • 初始化您的应用的关键数据结构。
  • 准备您的应用的窗口和视图以供显示:
    • 使用OpenGL ES进行绘图的应用程序不得使用这些方法来准备绘图环境。相反,将任何OpenGL ES绘图调用推迟到applicationDidBecomeActive:方法。
    • application:willFinishLaunchingWithOptions:方法中显示你的app窗口。 UIKit延迟使窗口,直到application:didFinishLaunchingWithOptions:方法返回才可见。

在启动时,系统会自动加载您的应用程序的主要storyboard文件并加载初始视图控制器。对于支持状态恢复的应用程序,状态恢复机制会将您的界面恢复到调用application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:方法之间的状态。使用application:willFinishLaunchingWithOptions:方法来显示您的应用程序窗口,并确定是否应该发生状态恢复。使用application:didFinishLaunchingWithOptions:方法对应用程序的用户界面进行最终调整。

您的application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:方法应该始终尽可能轻量级以减少应用程序的启动时间。应用程序需要启动,初始化自己,并在不到5秒的时间内开始处理事件。如果应用程序没有及时完成其启动周期,系统会因为无响应而杀死它。因此,任何可能减慢启动速度的任务(例如访问网络)都应该安排在子线程上执行。

1. The Launch Cycle - 启动周期

当您的应用程序启动后,它会从未运行状态转换为活动状态或后台状态,并在非活动状态中短暂转换。 作为启动周期的一部分,系统会为您的应用程序创建一个进程和主线程,并在该主线程上调用您的main函数。 Xcode项目附带的默认main函数会立即将控制权移交给UIKit框架,UIKit框架完成初始化应用程序并准备运行的大部分工作。

Figure 4-1显示了将应用程序启动到前台时发生的事件序列,包括所调用的应用程序代理方法。

Figure 4-1 Launching an app into the foreground

当您的应用程序启动到后台时(通常是为了处理某种类型的后台事件),启动周期稍微改变为图4-2所示。 主要区别在于,您的应用不是处于活动状态,而是进入后台状态以处理该事件,并可能在此之后的某个时刻暂停。 在启动到后台时,系统仍会加载应用的用户界面文件,但不会显示应用的窗口。

Figure 4-2 Launching an app into the background

要确定您的应用程序是启动到前台还是后台,请检查应用程序application:willFinishLaunchingWithOptions:application:didFinishLaunchingWithOptions:代理方法中UIApplication对象的applicationState属性。 当应用程序启动到前台时,此属性包含值UIApplicationStateInactive。 当应用程序启动到后台时,该属性将包含值UIApplicationStateBackground。 您可以使用此差异来相应地调整代理方法的启动时行为。

注意:启动应用程序以便它可以打开URL时,启动事件的顺序与图4-1和图4-2中所示的顺序略有不同。 有关打开URL时发生的启动顺序的信息,请参阅Handling URL Requests

2. Launching in Landscape Mode - 在横屏模式下启动

仅将横向取向用于其界面的应用程序必须明确要求系统以该方向启动应用程序。通常,应用程序将以纵向模式启动,并根据需要旋转其界面以匹配设备方向。对于同时支持纵向和横向的应用程序,请始终配置纵向模式的视图,然后让视图控制器处理任何旋转。但是,如果您的应用支持横向模式,但不支持纵向模式,请执行以下任务,使其初始以横向模式启动:

  • UIInterfaceOrientation键添加到应用程序的Info.plist文件中,并将此键的值设置为UIInterfaceOrientationLandscapeLeftUIInterfaceOrientationLandscapeRight
  • 在横向模式下布置您的视图,并确保其布局或自动调整选项设置正确。
  • 重写您的视图控制器的shouldAutorotateToInterfaceOrientation:方法,并为左或右横向方向返回YES,纵向方向返回NO。

应用程序应始终使用视图控制器view controllers来管理其基于窗口的内容。

Info.plist文件中的UIInterfaceOrientation键告诉iOS应该配置应用程序状态栏的方向(如果显示的话)以及任何视图控制器在启动时管理的视图的方向。 视图控制器考虑这个键并设置他们要匹配的视图初始方向。 使用此键相当于在执行applicationDidFinishLaunching:方法的早期调用UIApplicationsetStatusBarOrientation:animated:方法。

3. Installing App-Specific Data Files at First Launch - 首次启动时安装特定于应用程序的数据文件

您可以使用应用的首次启动周期来设置运行所需的任何数据或配置文件。特定应用程序的数据文件应该在应用程序沙箱的Library/Application Support/<bundleID>/,其中<bundleID>是您应用程序的bundle identifier。您可以进一步细分此目录以根据需要组织您的数据文件。您也可以在其他目录中创建文件,例如根据您的需要,将文件创建到应用程序的iCloud容器目录或本地Documents目录。

如果您应用的软件包中包含您计划修改的数据文件,请将这些文件复制出应用软件包并修改副本。您不得修改您的应用包内的任何文件。由于iOS应用程序是经过代码签名的,所以修改应用程序包内的文件会使应用程序的签名无效,并且会阻止您的应用程序在将来启动。将这些文件复制到Application Support目录(或沙箱中的另一个可写目录)并修改它们是安全使用这些文件的唯一方法。

有关应用程序相关数据文件放置位置的更多信息,请参阅File System Programming Guide


What to Do When Your App Is Interrupted Temporarily - 当你的应用程序暂时中断时该怎么办

Alert-based的中断会导致您的应用程序暂时失去控制权。 您的应用继续在前台运行,但不会从系统接收触摸事件。 (尽管如此,它仍会继续接收通知和其他类型的事件,例如加速度计事件。)为了响应此更改,您的应用程序应在applicationWillResignActive:方法中执行以下操作:

  • 保存数据和任何相关的状态信息。
  • 停止定时器和其他周期性任务。
  • 停止正在运行的元数据查询
  • 不要发起任何新的任务。
  • 暂停电影播放(除通过AirPlay播放外)。
  • 如果您的应用是游戏,请进入暂停状态。
  • 缩小OpenGL ES帧速率。
  • 挂起执行非关键代码的任何调度队列或操作队列。 (您可以在不活动时继续处理网络请求和其他时间敏感的后台任务。)

当您的应用程序移回活动状态时,其applicationDidBecomeActive:方法应该颠倒applicationWillResignActive:方法中采取的任何步骤。因此,重新激活后,您的应用程序应重新启动计时器,恢复调度队列,并再次提高OpenGL ES帧速率。但是,游戏不应该自动恢复;他们应该保持暂停,直到用户选择恢复他们。

当用户按下Sleep/Wake按钮时,带有受NSFileProtectionComplete保护选项保护的文件的应用程序必须关闭对这些文件的任何引用。对于配置了适当密码的设备,按下Sleep/Wake按钮将锁定屏幕,并强制系统丢弃启用了完整保护的文件的解密密钥。当屏幕锁定时,任何访问相应文件的尝试都将失败。所以,如果你有这样的文件,你应该在你的applicationWillResignActive:方法中关闭对它们的引用,并在你的applicationDidBecomeActive:方法中打开新的引用。

重要提示:始终将用户数据保存在应用程序的适当检查点。 虽然您可以使用应用程序状态转换来强制对象将未保存的更改写入磁盘,但不要等待应用程序状态转换以保存数据。 例如,管理用户数据的视图控制器应该在数据被dismiss时保存其数据。

1. Responding to Temporary Interruptions - 应对临时中断

当发生alert-based的中断时(例如来电),应用程序会暂时移至非活动状态,以便系统可以提示用户如何进行操作。 应用程序保持此状态,直到用户解除警报。 此时,应用程序会返回到活动状态或移动到后台状态。 图4-3显示了当发生基于警报的中断时,通过您的应用程序发生的事件流。

Figure 4-3 Handling alert-based interruptions

显示横幅banner的通知不会像基于警报的通知的方式停用您的应用。相反,横幅沿着您的应用程序窗口的顶部边缘放置,您的应用程序将继续像以前一样接收触摸事件。但是,如果用户拉下横幅以显示通知中心,则您的应用将移至非活动状态,就像发生基于警报的中断一样。您的应用会保持非活动状态直到用户关闭通知中心或启动其他应用程序。此时,您的应用程序会移至相应的活动或后台状态。用户可以使用Settings设置来配置哪些通知显示横幅,哪些显示alert。

按下Sleep/Wake按钮是另一种类型的中断,会导致您的应用暂时停用。当用户按下此按钮时,系统禁用触摸事件,将应用程序移动到后台,将应用程序的applicationState属性的值设置为UIApplicationStateBackground,并锁定屏幕。锁定的屏幕对使用数据保护来加密文件的应用程序有额外的后果。这些结果在What to Do When Your App Is Interrupted Temporarily中有描述。


What to Do When Your App Enters the Foreground - 当您的应用程序进入前台时该怎么做

回到前台,您的应用程序有机会重新启动它移至后台时停止的任务。 移动到前台时发生的步骤如图4-4所示。 applicationWillEnterForeground:方法应该撤销在applicationDidEnterBackground:方法中完成的任何操作,并且applicationDidBecomeActive:方法应该继续执行与启动时相同的激活任务。

Figure 4-4 Transitioning from the background to the foreground

注意:UIApplicationWillEnterForegroundNotification通知也可用于跟踪您的应用何时重新进入前台。 您应用中的对象可以使用默认通知中心注册此通知。

1. Be Prepared to Process Queued Notifications - 准备处理排队的通知

处于挂起状态的应用程序必须准备好在返回到前台或后台执行状态时处理任何排队的通知。暂停的应用程序不会执行任何代码,因此无法处理与方向更改,时间更改,首选项更改以及会影响应用程序外观或状态的许多其他方面有关的通知。为了确保这些更改不会丢失,只要系统重新开始执行代码(无论是在前台还是后台),系统就会排队许多相关通知并将其发送到应用程序。为了防止您的应用在恢复时被通知重载,系统会合并事件并发送单个通知(每种相关类型),以反映应用暂停后的净变化。

表4-1列出了可以合并并传送到您的应用程序的通知。这些通知中的大部分都直接发送给注册的观察员。有些与设备方向更改相关的内容通常会被系统框架拦截并以另一种方式传递给您的应用

Table 4-1 Notifications delivered to waking apps

排队的通知在您的应用程序的主运行循环中提供,并且通常在任何触摸事件或其他用户输入之前交付。 大多数应用程序应该能够快速处理这些事件,以便在恢复时不会引起任何明显的延迟。 但是,如果您的应用在从后台状态返回时显示缓慢,请使用Instruments确定是否是您的通知处理程序代码导致的延迟。

返回前台的应用程序还会接收自上次更新后标记为脏的任何视图的视图更新通知。 在后台运行的应用程序仍然可以调用setNeedsDisplaysetNeedsDisplayInRect:方法来请求更新其视图。 但是,由于视图不可见,因此只有在应用程序返回到前台后,系统才会合并请求并更新视图。

2. Handle iCloud Changes - 处理iCloud更改

如果iCloud的状态因任何原因而发生更改,系统会向您的应用程序发送NSUbiquityIdentityDidChangeNotification通知。 当用户登录或退出iCloud帐户或启用或禁用文档和数据同步时,iCloud状态会发生变化。 此通知是您的应用更新缓存以及任何与iCloud相关的用户界面元素以适应更改的提示。 例如,当用户注销iCloud时,应该删除对所有基于iCloud的文件或数据的引用。

如果您的应用程序已经提示用户是否将文件存储在iCloud中,则在iCloud状态更改时不要再次提示。 在第一次提示用户之后,将用户的选择存储在应用程序的本地偏好设置中。 然后,您可能希望使用Settings bundle或作为应用中的选项公开该首选项。 但不要再次重复提示,除非该首选项当前不在用户默认数据库中。

3. Handle Locale Changes - 处理区域设置更改

如果用户在应用程序挂起时更改当前区域设置,则可以使用NSCurrentLocaleDidChangeNotification通知在应用程序返回到前台时强制更新任何包含区域设置敏感信息(例如日期,时间和数字)的视图。 当然,避免与语言环境相关的问题的最好方法是以易于更新视图的方式编写代码。 例如:

  • 检索NSLocale对象时使用autoupdatingCurrentLocale类方法。 此方法返回一个区域设置对象,该对象会自动更新以响应更改,因此您不需要重新创建它。 但是,当语言环境更改时,您仍然需要刷新包含从当前语言环境派生的内容的视图。
  • 每当当地语言环境信息发生变化时,重新创建任何缓存的日期和数字格式化程序对象。

有关使代码国际化以处理区域设置更改的更多信息,请参阅Internationalization and Localization Guide

4. Handle Changes to Your App’s Settings - 处理对应用程序设置的更改

如果您的应用具有由Settings应用管理的设置,则应该遵守NSUserDefaultsDidChangeNotification通知。由于用户可以在应用程序被暂停或在后台修改设置,因此您可以使用此通知来响应这些设置中的任何重要更改。在某些情况下,回复此通知可帮助消除潜在的安全漏洞。例如,电子邮件程序应该响应用户帐户信息的变化。未能监视这些更改可能会导致隐私或安全问题。具体而言,即使该帐户不再属于该人,当前用户也可以使用旧帐户信息发送电子邮件。

在收到NSUserDefaultsDidChangeNotification通知后,您的应用程序应重新加载任何相关设置,并在必要时适当重置其用户界面。在密码或其他安全相关信息发生变化的情况下,您还应该隐藏以前显示的任何信息并强制用户输入新密码。


What to Do When Your App Enters the Background - 当您的应用程序进入后台时应该怎么做

从前台移动到后台时,请使用应用程序委托的applicationDidEnterBackground:方法执行以下操作:

  • Prepare to have your app’s picture taken - 准备好拍摄您应用的照片

    • applicationDidEnterBackground:方法返回时,系统会拍摄应用程序用户界面的图片,并将结果图像用于过渡动画。如果界面中的任何视图包含敏感信息,则应在applicationDidEnterBackground:方法返回之前隐藏或修改这些视图。如果您将新视图作为此过程的一部分添加到视图层次结构中,则必须强制这些视图进行绘制,如Prepare for the App Snapshot中所述。
  • Save any relevant app state information - 保存任何相关的应用状态信息

    • 在进入后台之前,您的应用程序应该已经保存了所有重要的用户数据。使用过渡到后台来保存对应用程序状态的任何最后更改。
  • Free up memory as needed - 根据需要释放内存

    • 释放您不需要的任何缓存数据,并执行任何可能减少应用内存占用的简单清理。具有大内存占位符的应用程序是系统首先终止的应用程序,因此释放不再需要的图像资源,数据缓存和任何其他对象。有关更多信息,请参阅 Reduce Your Memory Footprint

您的应用程序代理的applicationDidEnterBackground:方法大约需要5秒来完成任何任务并返回。实际上,这种方法应该尽快返回。如果该方法在时间耗尽之前未返回,则应用程序将被终止并从内存中清除。如果您仍然需要更多时间来执行任务,请调用beginBackgroundTaskWithExpirationHandler:方法来请求后台执行时间,然后在辅助线程中启动任何长时间运行的任务。无论您是否启动任何后台任务,applicationDidEnterBackground:方法仍必须在5秒内退出。

注意:除了调用applicationDidEnterBackground:方法之外,系统还会发送UIApplicationDidEnterBackgroundNotification通知。 您可以使用该通知将清理任务分配给应用程序的其他对象。

根据应用程序的功能,应用程序在移动到后台时应该执行其他操作。 例如,任何有效的Bonjour服务应该被暂停,应用程序应该停止调用OpenGL ES函数。 有关移动到后台时应执行的操作列表,请参阅Being a Responsible Background App

1. The Background Transition Cycle - 后台转换周期

当用户按下Home按钮,按下Sleep/Wake按钮,或系统启动另一个应用程序时,前台应用程序将转换到非活动状态,然后转换到后台状态。 这些转换导致调用应用程序委托的applicationWillResignActive:applicationDidEnterBackground:方法,如图4-5所示。 从applicationDidEnterBackground:方法返回后,大多数应用程序在不久之后移至暂挂状态。 请求特定后台任务(例如播放音乐)或从系统请求一点额外执行时间的应用程序可能会继续运行一段时间。

Figure 4-5 Moving from the foreground to the background

2. Prepare for the App Snapshot - 准备App快照

在应用程序代理的applicationDidEnterBackground:方法返回后不久,系统会拍摄应用程序窗口的快照。同样,当应用程序被唤醒以执行后台任务时,系统可能会拍摄新快照以反映任何相关更改。例如,当应用程序被唤醒以处理下载的项目时,系统会拍摄新的快照,以便反映由于合并项目而导致的任何更改。系统在多任务界面中使用这些快照图像来显示您的应用程序的状态。

如果您在进入后台后对视图进行了更改,则可以调用主视图的snapshotViewAfterScreenUpdates:方法以强制渲染这些更改。在视图上调用setNeedsDisplay方法对于快照是无效的,因为快照是在下一个绘制周期之前拍摄的,因此可以防止渲染出任何更改。调用值为YES的snapshotViewAfterScreenUpdates:方法会立即更新快照机制使用的基础缓冲区。

3. Reduce Your Memory Footprint - 减少你的内存占用

在进入后台时,每个应用程序都应释放尽可能多的内存。系统会尝试尽可能多地将内存中的应用程序保存在内存中,但是当内存不足时,它会终止暂停的应用程序来回收内存。在后台消耗大量内存的应用程序是第一批要终止的应用程序。

实际上,您的应用程序在不再需要时应立即删除对象的强引用。删除强引用使编译器能够立即释放对象,以便可以回收相应的内存。但是,如果要缓存某些对象以提高性能,则可以等到应用程序转换到后台时再删除对它们的引用。

尽快删除强引用的对象的一些示例包括:

  • 您创建的图像对象。 (UIImage的某些方法返回图像,其基础图像数据由系统自动清除,有关详细信息,请参阅UIImage Class Reference中的讨论。)
  • 大型媒体或数据文件,您可以从磁盘再次加载
  • 您的应用不需要的任何其他对象,并且可以在以后轻松地重新创建

为了帮助减少应用程序的内存占用量,当您的应用程序移动到后台时,系统会自动清除代表应用程序分配的一些数据。

系统清除所有Core Animation图层的后备存储。这项工作不会从应用程序中删除应用程序的图层对象,也不会更改当前的图层属性。它只是防止这些图层的内容出现在屏幕上,因为应用程序在后台不应该发生。

  • 它删除任何系统对缓存图像的引用。
  • 它删除了对其他一些系统管理的数据高速缓存的强引用。

后记

本篇主要讲述了处理应用程序状态转换的策略,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容