刘浩的技术博客

就当是做个笔记,顺便分享一些知识,更希望业界的交流

APP生命周期与后台执行

引言

此篇文章几乎是对照着Apple文档翻译,某些地方是按照自己理解翻译的,难免有理解和翻译不当的地方,在此告知。

APP生命周期(The App Life Cycle)

APP是在你的自定义代码和系统框架中间复杂的相互作用。这个系统框架提供了所有的APP运行需要的基础结构,你提供的代码需要去定制这个结构和给APP一个样子和你想要的感觉。有效的做这个,它帮助你了解一点点关于iOS的结构和它如何工作的 iOS框架依赖于设计模式,例如MVC和代理在他里面的实现。了解这些设置模式是成功创建一个APP的关键。他同样帮助你熟悉objective-c语言和它的特性。

APP的结构(The Structure of an App)

在启动的时候,这个UIApplicationMain函数建立几个关键的对象和开始这个APP运行。每一个APP的核心是UIApplication对象,它的工作是在APP中去便利系统和另外对象的交互。iOS应用都采用了mvc结构,这个模式用来数据视觉呈现时的数据和业务逻辑的分离。这个是去创建APP运行在不同的设备的不同屏幕尺寸上的关键结构。

iOS应用中各个对象的角色

UIApplication对象

UIApplication对象管理事件的循环和另外的高等级的行为。它同样传达关键的APP迁移和一些特定的事件(例如正到来的推送通知)给它的代理。

App delegate对象

这个App delegate是你自定义代码的核心。这个对象和UIAplication对象协力去操作APP的初始化,状态迁移,和许多高等级APP事件。这个对象同样保证在每一个APP中仅仅一个,如此它通常用来建立APP的初始数据结构。

Documents和data model对象

Data model对象存储了你的APP内容并且是特定于你的APP的。APP能同样使用document对象(自定义UIDocument子类)去管理一些或者所有的他们的data model对象。Document对象不是必须的,但是它提供一种方便的方法去组织你的数据到一个单个的文件或者文件包中。

View controller

UIViewController是所有View controller对象的基类。它提供了默认的功能关于加载视图,呈现他们,旋转他们去响应设备的旋转,和几个另外的标准的系统行为。UIKit和另外的框架定义了额外的View controller类去实现标准的系统界面,例如图片选择器(image picker),tab bar界面和导航界面。

UIWindow对象

UIWindow对象协调一个或多个视图的显示。大部分的APP只有一个window,它在主屏幕上呈现内容,但是APP可能有一个额外的视图关于显示内容到一个外部的显示。 去改变你的APP的内容,你使用view controller去改变视图在相应的window里的显示。你永远不可能替换window本身。 除了承载视图,Windows和UIApplication一起工作去交付事件给你的视图和view controllers。

View对象,control对象和layer对象

Views和controls提供你的APP的内容的视觉表现。一个view是一个对象,它绘制内容在一个指定的矩形区域,响应这个指定的矩形区域中的事件。Controls是一个专门的view类型,负责实现熟悉的界面对象,例如buttons、text field和toggle switches。

APP执行状态(Execution States for Apps)

在任何一个给定的时刻,你的APP都是在下面的任意一个状态。系统在整个系统运行期为了响应动作的发生去迁移你的APP从一个状态到另外一个状态。例如用户按了home键,一个电话打进来或者任何几个其他的中断发生,当前运行的APP响应后去改变自己的状态。一下是一些APP各状态描述。

没有运行(Not running)

这个APP没有启动或者在运行但是通过启动终结了。

未活动(Inactive)

这个APP运行在前台但是现在不接受事件。(它可能执行另外的代码)一个APP通常只停留在这个状态一瞬间然后他迁移到不同的状态。

活动(Actice)

APP运行在前台并且接受事件。这个是一个前台APP标准的模式。

后台(Background)

APP后台和执行代码,大部分APP进入这个转态一瞬间就被系统挂起。无论如何,一个APP请求一个额外的执行时间能够保持在这个状态一段时间。另外,一个APP启动时直接进入后台状态而不是未活动(Inactive)状态.

挂起(Suspended)

APP进入后台但是没有执行代码,系统自动移动APP到挂起状态并且做这个的时候没有通知。在挂起的期间,APP保持在内存里但是不执行任何代码。 当一个低内存状况发生,系统可能净化挂起的APP去制造更多的空间给前台APP并且做这个的时候没有通知。 大部分的状态迁移都伴随着APP代理方法的调用。这些方法是给你机会用一些适当的方式去响应状态的改变。以下是这些方法的列表。

application:willFinishLaunchingWithOptions:

这个方法是在APP启动时第一次机会去执行代码。

application:didFinishLaunchingWithOptions:

这个方法是你的APP显示给用户之前运行你执行任何最终的初始化处理。

applicationDidBecomeActive:

让你知道你的APP已经变为了前台APP。这个方法给你最后一分钟的准备。

applicationWillResignActive:

让你知道你的APP将迁移离开前台APP,使用这个方法把你的APP变为一个静态状态。

applicationDidEnterBackground:

让你知道你的APP现在运行在后台状态并且随时可能挂起。

applicationWillEnterForeground:

让你知道你的APP已经移动退出了后台状态和返回进入了前台,但是这个还不是活动的状态。

applicationWillTerminate:

让你知道你的APP开始终结。如果你的APP挂起了,那么不会调用这个方法。

后台执行(Background Execution)

当用户没有积极的使用你的app时,系统会移动这个app到后台状态,许多的APP,在后台状态事实是驻留一瞬间就被系统迁移到挂起状态了。挂起APP这个做法是为改善电池寿命和把系统的重大资源用于新的前台APP以用于吸引用户的手段。 大部分的APP能够相当容易的迁移到挂起状态但是也同样有正当的持续在后台运行的理由。例如一个徒步旅行APP可能想要追踪用户在任何时间的位置这样能够显示航线在徒步旅行地图上面。一个音频APP可能需要在锁屏的时候继续播放音乐。另外一些APP可能需要在后台下载内容如此它能以最小的延迟展现这个内容给用户。当你认为需要你的APP能够在后台运行,iOS帮助你有效的做这些并不耗尽系统资源和用户电池。这个技术通过iOS分成三类提供。

  1. APP在前台启动一个短的任务,当APP迁移到后台时能够请求时间去结束这个任务。
  2. APP在前台开始下载能够挪动那些下载的管理给系统管理,从而在下载期间允许APP挂起或者终结。
  3. APP需要在后台运行指定的任务类型,可以声明他们要支持的一个或者多个后台任务模式。

总是试图去避免做任何后台工作,除非这么做能够改善整体的用户体验。一个APP迁移到后台的可能的原因有用户启动了一个不同的APP或者因为用户锁定了设备和此时没有使用这个APP.在这两种情况,用户此时不需要你的APP做任何有意义的工作。在这样的条件下继续运行你的APP仅仅是耗尽电池和可能导致用户去强制完全退出你的APP。如此记住在后台你做的工作当你能避免的时候避免。

执行有限长度的任务(Executing Finite-Length Tasks)

APP迁移到后台是预期把自己尽可能快的进入到一个静止的转态,如此系统能够暂停他们。如果APP是在一个任务运行的中间和需要一点时间去完成这个任务,它能够调用UIApplication对象的 beginBackgroundTaskWithName:expirationHandler: 或者beginBackgroundTaskWithExpirationHandler:方法去请求一些额外的执行时间。调用这两个方法中的任何一个都可以暂时去延迟你的APP的挂起,给它一点额外的时间去结束这个工作。到这个工作完成的时候,你的APP必须调用endBackgroundTask:方法去让系统知道任务结束了系统能够挂起这个APP了。 每次调用beginBackgroundTaskWithName:expirationHandler: 或者beginBackgroundTaskWithExpirationHandler:方法生成一个唯一的令牌(token)去关联相应的任务。但你的APP完成了一个任务的时候,它必须用相应任务的令牌(token)作为参数去调用endBackgroundTask:方法去让系统知道这个任务完成了。失败去调用endBackgroundTask:方法关于一个后台任务将导致你的应用程序终结。当开始这个任务如果你提供了一个到期终止处理(expiration handler),这个系统调用这个到期终止处理(expiration handler)给你一个最后的机会去结束任务和避免APP终结。 你不需要去等到你的APP迁移到后台的时候再去指派后台任务。开始一个任务之前,一个更有用的设计是调用beginBackgroundTaskWithName:expirationHandler: 或者 beginBackgroundTaskWithExpirationHandler:方法。等到你的任务结束就调用endBackgroundTask:方法。

在你自己的到期终止处理(expiration handler)中,你能够包含额外的需要去关闭你的任务的代码。你包含的任何代码不能拿太长时间去执行,因为到你调用到期终止处理(expiration handler)时候,你的APP已经很靠近它的时间限制了。因为这个原因,仅仅执行极少的你的状态信息的清理和结束这个任务。

在后台下载内容(Downloading Content in the Background)

但下载文件时,APP应该使用一个NSURLSession对象去开始一个下载,如果这个APP挂起或者终结此时系统能拿到这个下载处理的控制。当你配置一个NSURLSession对象作为后台传输用,系统在一个独立的处理里面管理那些传输和以通常的方式报告状态给你的APP。当以传输在进行期间如果你的APP终结了,系统在后台继续这个传输,当这个传输结束或者当一个或多个任务需要你的APP的注意,启动这个APP(当适当的时候)。 去支持后台传输,你必须适当的配置你的NSURLSession对象,去配置这个回话(session),你必须首先创建一个NSURLSessionConfiguration对象和设置这个对象的几个属性到适当的值。当创建这个回话(session)的时候,你接着传递那个配置对象到NSURLSession的适当的初始化方法。

创建支持后台下载配置对象的过程如下:

  1. 使用NSURLSessionConfiguration的 backgroundSessionConfigurationWithIdentifier: 方法创建配置对象。
  2. 设置配置对象的sessionSendsLaunchEvents属性值为YES.
  3. 如果你的APP在前台期间启动的下载,推荐你同样设置配置对象的discretionary属性值为YES。
  4. 配置这个配置对象任何其他的属性值到适当的值。
  5. 使用这个配置对象去创建你的NSURLSession对象。

一单配置,你的NSURLSession对象在适当的时候无缝移交这个上传和下载任务给系统。在APP仍然在运行的时候如果你的任务结束(前台和后台任其一的状态),这个回话(session)对象以通常的方式通知它的代理(delegate)。如果这个任务尚未完成此时系统终结了你的APP,这个系统自动的在后台继续管理这个任务。如果用户终结了你的APP,系统取消所有的待执行的任务。

实现长运行任务(Implementing Long-Running Tasks)

对于需要更多执行时间去实施的任务,你必须请求指定的许可(specific permissions )在后台去运行他们,没有他们APP在后台会被暂停。iOS中,只有特定的APP类型能被允许在后台运行:

  1. 在后台播放有声音的内容给用户的APP,例如音乐播放APP。
  2. 在后台录制音频内容的APP。
  3. 让用户随时了解他们的位置的APP,例如导航APP。
  4. 支持VoIP(Voice over Internet Protocol)的APP。
  5. 接收来自外部附件定期更新的APP。

实现这些服务(services)的APP必须声明相应的服务,他们支持和使用系统框架去实现那些服务相关的方面。声明服务让系统知道你使用了哪一种服务,但在某些情况下,系统框架实际阻止了你的APP被暂停。

声明你APP支持的后台任务(Declaring Your App’s Supported Background Tasks)

支持一些后台执行类型必须通过你的APP提前声明使用它们。在Xcode5以后,你在你项目设置的“Capabilities”标签中声明你的APP所支持的后台模式。启用这个后台模式选项添加UIBackgroundModes键到你的APP的Info.plist文件中。选择一个或者多个检验盒(checkboxes )添加相应的后台模式值到该键。你能指定以下的后台模式,Xcode会分配这个值到你的APP的Info.plist文件的UIBackgroundModes键中。

  1. Audio和AirPlay。
  2. 位置更新
  3. VoIP(Voice over IP)
  4. Newsstand下载。
  5. 外部附近通信。
  6. 使用蓝牙LE附件。
  7. Acts as a Bluetooth LE accessory。
  8. 后台拉取。
  9. 远程通知。

每一个前面的模式让你的系统知道你的APP应该在适当的时间唤醒或者启动去相应相关的事件。

在后台获得用户的注意(Getting the User’s Attention While in the Background)

通知是一个当你的APP挂起、在后台或者没有运行需要获得用户注意的方式。APP能够使用本地通知去显示通告,播放声音,以APP的图标为徽章显示,或者这三个的组合。 排定一个本地通知的交付,创建一个UILocalNotification类的实例,配置这个通知的参数,使用UIApplication类的方法计划他。

选择退出后台执行

如果你不想要你的APP在任何时候运行在后台,你能够通过添加UIApplicationExitsOnSuspend键(设置值为YES)到你的APP的Info.plist文件来明确的选择退出后台。当一个APP是这样设置的,它将循环在没有运行(not-running),未活跃的(inactive),和活跃(active)状态之间,并且从不进入后台或者挂起状态。当用户按了Home键去退出这个APP,这个APP的代理方法 applicationWillTerminate:会被调用,他终结以前APP大约有五秒时间去清理和退出,迁移到没运行(not-running)的状态。 选择退出后台执行时强烈反对的,但是可能在一定条件下是首选的。