原文翻译Strategies for Handling App State Transitions

在app的任何的一个运行状态,系统在你的app处于不同状态时有不同的要求。当状态改变发生时,系统在app delegate中通知app。你可以用the UIApplicationDelegate protocol 的方法去监测到状态的改变并适当的做出反应。例如,当状态发生从前台到后台的改变时,你可以保存一些数据或是停止正在进行的任务。下面为大家提供现状态改变时编程代码的规范和建议。

当App启动时做什么

当你的app启动时(不论进入到foreground还是后台),用application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: 这两个方法去做如下事情:

  • 检查launch option 字典的内容去拿到app启动的信息,包括为甚么app会被启动,并且恰当的进行处理。
  • 初始化你app的重要数据结构
  • 准备你的app即将展示的window和views:
    1. app 用OpenGL ES来绘图的,一定不能用这两来方法去绘图。应该用 applicationDidBecomeActive: 这个方法。
    2. 在application:willFinishLaunchingWithOptions: 方法中显示你的window。UIKit 不会使window可视化,直到application:didFinishLaunchingWithOptions
      方法执行完。

在启动时,系统自动加载你app的main storyboard 文件,并且加载其初始控制器。对于那些支持状态恢复的app,状态恢复机制储存你的界面,在application:willFinishLaunchingWithOptions: 和application:didFinishLaunchingWithOptions:方法之间的的状态时时使用。用application:willFinishLaunchingWithOptions
方法去展现你的window和决定是否进行状态修复。用 application:didFinishLaunchingWithOptions:
方法去做界面的最后调用。
application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: 方法应该尽可能的轻量级这样会使启动时间尽量减少。App在启动、初始化和处理事件这些事情应该少于5秒钟。如果app没有适时的结束它的启动环节,系统将因为app的反应迟钝而杀死他。因此那些会使app减缓启动速度的任务应该安排在子线程中处理。

启动环节

当你的app启动时,它会从not running 状态进入到active状态,或者是进入到background状态,inactive状态只是一个过渡状态。作为启动环节的一部分,系统创建了一个程序和主线程并且在主线程唤起app的main函数。默认的主函数和xcode工程 将控制权交给UIKit frameWork,UIKit frameWork在初始化app和准备运行app时做了大部分的工作。
Figure 4-1 展现了当app从启动到进入前台的一系列事件,包括app delegate 的调用。

当你的app启动并进入到后台-通常去处理一些后台事件类型-如Figure 4-2 展示的。跟上一个介绍的启动类型不同的是,你的app不会进入active状态,而是进入background状态去处理事件,并且在处理完事件后的某个时间点会被挂起。当app启动并进入后台,系统仍然加载app的用户界面文件,但是并没有展示在app的window上。

为了能够辨别app启动后是进入前台还是后台,去检测 application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions:方法中的UIApplication 对象的applicationState属性。当app启动后进入前台,这个属性包含值UIApplicationStateInactive.当你的app进入到后台,这个属性包含值 UIApplicationStateBackground 。你可以利用这处不同,来调整app在启动时的不同操作。

Note:

当你的app是由其他的app呼起的,一系列启动事件回合Figure4-1 和Figure 4-2 有所不同。更多的细节请参阅Handling URL Requests.

横屏模式启动

那些只有横屏模式界面的app必须让系统在启动app时是在横屏模式下。通常app是在竖屏情况下启动的,并且配合设备的旋转方向旋转你的界面,并且让你的controller来处理所有旋转。如果你的app支持横屏但是不支持竖屏,处理方式如下:
- 加入 UIInterfaceOrientation key 在app的 Info.plist 文件中,并且对这个key进行赋值UIInterfaceOrientationLandscapeLeft or UIInterfaceOrientationLandscapeRight。
- 布局好所有在横屏模式下的视图。
- 重写控制器的 shouldAutorotateToInterfaceOrientation: 方法并且在left 、right 方向上return YES,在竖直方向上return No;

重点:

app应该一直使用controller来处理 基于window的内容。
UIInterfaceOrientation 这个在Info.plist 中的key在app启动时,告诉app他的状态栏的方向以及它的视图方向。Controller遵守这个key的内容并且设置view的初始化方向。使用这个key相当于UIApplication在applicationDidFinishLaunching:
前调用了setStatusBarOrientation:animated:方法。

当App暂时被打断时做什么?

Alert类的打断导致了你的app的暂时不受控制。你的app继续在前台运转,但是收不到触摸事件。并且app确实还会继续接受通知和其他类型的事件,例如加速器事件。为了对这种改变做出相应反应,你的app应该在applicationWillResignActive: method:方法中这样做:
- 保存数据和相关状态信息
- 停止计时器和其他周期性任务
- 停止数据查询
- 不要初始新的任务
- 停止媒体播放
- 如果你的app是一个游戏,进入停止状态
- 减缓OpenGL ES frame 的速率。
- 暂停多线程执行的不重要的代码。(你可以继续进行网络请求和其他时效性的后台任务)
当你的app进入到active 状态时, applicationDidBecomeActive:方法应该做applicationWillResignActive: 方法中做的相反的事情。你的app应该重启计时器,重启多线程。。。然而游戏不应该自动继续,他们应该保持暂停状态直到用户选择继续。
当用户按了锁屏/唤醒按钮,有被 NSFileProtectionComplete 保护机制的所保护的文件的app ,必须关掉那些文件。那些有密码的设备,在按下锁屏/唤醒按钮的时候锁住了屏幕并且强迫系统丢弃调那些可以破译文件的key来完成文件保护。当屏幕锁上,任何尝试读取这些文件的操作都会失败。所以如果你有类似的文件,你应该在applicationWillResignActive: 方法中关闭,并在applicationDidBecomeActive:中开启。

重点:

你要在适当时机储存好你的用户数据。尽管你可以在app状态改变时去将未储存的变化记录下来,但是永远不要在仅仅等到app状态改变时才记得去储存数据。例如一个controller应该在它dismiss时就将数据储存好。

打断程序时的反应

当一个提示框中断发生时,例如一个打进来的电话,app暂时变为inactive状态,这样系统可以促使用户去判断下一步做什么。App保持这个状态直到用户dismiss这个alert。在此时app要不就是回到active状态,要不就是进入后台状态。Figure 4-3 展示了这一过程。

通过在导航栏上部展示的通知并不会使app进入inactive状态。这种通知栏会占用你app window的上部边缘部分并且你的app可以正常响应你的手势。但是如果用户下拉了这个通知栏去展示通知中心,你的app将进入inactive状态就像被“提示框中断”打断那样。你的app会一直在inactive状态,直到用户dismiss通知中心或者启动其他的app。在此时你的app将要进入active状态或者background状态。用户可以在设置中设定app的通知方式以通知栏显示还是以提示框方式显示。
当按下锁屏/唤醒按钮时,是另外一种使app进入不活跃状态的打断app的方式。当用户按下这个按钮时,系统将使触摸事件失效,将app放入background状态,把App的applicationState 属性设置为 UIApplicationStateBackground,状态,并且锁上屏幕。锁屏会导致一些使用数据保护文件方式的app有一些附加的结果。那些结果被描述在上一部分中。

当app进入到前台时做什么?

从后台回到了前台的时机是重启那些在进入后台中断任务的机会。从后台进入到前台的步骤如Figure 4-4所示。 applicationWillEnterForeground: 方法应该做与applicationDidEnterBackground:方法中所做的相反的事情。 applicationDidBecomeActive:方法应该继续运行和app启动时开启时自己开启的任务。

注意:

UIApplicationWillEnterForegroundNotification 通知也可以追踪app重新回到前台的状态。你可以在notification center里去注册这个通知。

准备好按顺序处理通知

一个app属于挂起状态必须准备好在app回到前台或者后台去处理所有被排好队的通知。一个挂起的app不执行任何代码,因此不能处理任何通知,包括设备方向变化,时间变化,偏好的变化和一些其他的影响app的外观及状态的通知。为了保证这些变化没有被弄丢,系统会把相关的通知安排在队列中并且在app可以处理代码时(不管是前台还是后台)将他们传递给app。以防app在重新开始处理任务时超负荷处理通知,系统在app被挂起时合并了事件,并且将一个反映了纯粹的变化的通知(每种相关类型合并成一个通知)传递给app。

Table4-1列出了可以被合并并传递给你app的通知。大部分通知直接传递给了注册的观察者。一些通知像有关设备方向改变这种通知,被系统的framework拦截并被通过另外一种方式传递到你的app。

Table 4-1

设备方向的变化 UIDeviceOrientationDidChangeNotification除了这个通知,controller 会自动更新app的界面
附件的接入和接出 EAAccessoryDidConnectNotification、EAAccessoryDidDisconnectNotification
重要的时间变化 UIApplicationSignificantTimeChangeNotification
电池量和电池状态的改变 UIDeviceBatteryLevelDidChangeNotification、UIDeviceBatteryStateDidChangeNotification
电话屏幕接近状态的改变 UIDeviceProximityStateDidChangeNotification
被保护文件状态的改变 UIApplicationProtectedDataWillBecomeUnavailable、UIApplicationProtectedDataDidBecomeAvailable
外界显示的接入和接出 UIScreenDidConnectNotification、UIScreenDidDisconnectNotification
ScreenMode改变 UIScreenModeDidChangeNotification
暴露给系统Setting里的偏好的改变 NSUserDefaultsDidChangeNotification
语言和位置设置的改变 NSCurrentLocaleDidChangeNotification
iCloud账户的改变 NSUbiquityIdentityDidChangeNotification

被排好队的通知将传递给app的main run loop 并且是先于触摸和其他用户输入的。大多数app在重新开始处理事务时应该能够快速处理这些事件,不会引起被人察觉的拖延。然而,如果你的app在从后台回到前台时慢吞吞的,用instrument 确定下是否是你的通知处理代码导致的延迟。
一个app回到前台也会接受到对那些在最后一次刷新后被标记为脏视图的视图进行更新的通知。一个app在后台运行一样可以调用 setNeedsDisplay or setNeedsDisplayInRect: 方法去进行视图的刷新请求。但是因为视图不可见,系统会合并这些请求并且在app返回前台时进行对视图的刷新。

  • 处理地理位置的变化
    如果用户在你的app在挂起时改变了所处的地理位置,你可以用NSCurrentLocaleDidChangeNotification 通知在app回到前台时去强制更新包含位置敏感信息的视图,例如更改日期、时间等信息。最好的避免地理位置相关问题的方法是用一种容易更新视图的方式去写代码。例如:

    使用autoupdatingCurrentLocale 类去获取NSLocale 对象。这个方法返回一个位置对象,这个对象在遇到变化时会更新自己,所以你不用去重新创建它。然而,当地理位置改变时,你仍然需要刷新那些包含当前地理位置信息的视图。
    当前地理信息改变时重新创建被储存的数据和数据格式化对象。
    更多信息请参阅 Internationalization and Localization Guide.

  • 处理你app 设置的改变
    如果你的app相关设置被app的设置功能所改变,app应该监听NSUserDefaultsDidChangeNotification 通知。因为当app挂起时或它在后台,用户可以修改设置里的项,你可以用这个通知监听设置的改变。在一切情况中,对这些通知做出反应可以减少潜在性的安全漏洞。例如一个email 项目应该对用户的账户信息的改变做出反应。如果没有处理这些变化将造成隐私安全问题。具体来说,当前的用户肯能会用老用户账号发送邮件,即使这个账户不属于现在的用户。
    对于收到NSUserDefaultsDidChangeNotification通知,你的app应该重新加载相关设置,如果可能,重新妥当的设置用户界面。如果是密码或者是其他安全相关的信息改变,你应该遮挡原来的展示界面,并强制让用户重新输入密码。

当app进入后台时做什么?

当从前台到后台时,用 the applicationDidEnterBackground: 方法去做以下的事情:
1. 准备使你的app的图像被截取。当applicationDidEnterBackground: 方法返回,系统将获取一张app的用户界面并且用这张图片作为app的过渡动画。如果在你的界面中的视图含有敏感信息,你应该在applicationDidEnterBackground: 方法return前遮挡或者修改那些视图。如果你在你视图层级上加入一些新的视图作为这个过程的一部分,你应该强制那些视图重绘如在 Prepare for the App Snapshot描述的一样。
2. 保存app的相关状态的信息。在进入background前,你的app应该储存好重要的信息。并且储存app在改变状态前的状态。
3. 释放内存。释放一些你不需要诗句并且做一些可以减到你app内存台面空间的简单清理。有很大内存台面空间的将会被系统第一时间终止,所以释放你的图片资源,数据储存和其他你不在需要的对象。更多信息请查看 Reduce Your Memory Footprint。

你app的 applicationDidEnterBackground:有将近5秒的时间去结束任务并且return。事实上这个方法应该尽快返回。如果方法在时间限制前没有return ,你的app将被杀死并且释放掉内存。如果你仍然需要实现一些任务,实现beginBackgroundTaskWithExpirationHandler
这个方法去请求后台执行时间,然后在第二条子线程中开启一个长时间运行的任务。无论你是否开启后台任务,applicationDidEnterBackground
必须在5秒内退出。

注意:

你也可以用UIApplicationDidEnterBackgroundNotification 通知来代替appdelegate的方法
根据你app的特点,当你的app进入后台时你可能需要做一些其他的事情。例如 Bonjour services 应该被挂起,并且app应停止调用OpenGL ES functions。你的app应该做的一系列的事情,请参阅 Being a Responsible Background App.

后台变换状态的流程

当你的用户按了home键,按了锁屏/唤起键,或者系统启动了其他的app,前台的app变换为inactive状态并且进入到background状态。这些状态的变化导致了app delegate调用applicationWillResignActive: 和applicationDidEnterBackground:方法,如Figure 4-5 所示。在 applicationDidEnterBackground:方法return 后,大多数app进入到挂起状态。App需要执行后台任务时(例如音乐播放)或者需要一些额外的执行时间,app 会继续运行一段时间。

准备app的快照

当 applicationDidEnterBackground: 方法return后,马上系统会为app的window做一个快照。简单来说,当app被唤起去执行后台任务,系统会重新为app的改变来做一个新的app快照。例如当一个app被唤醒去去进行下载任务,系统会对app做一个快照,这样可以反映这个任务带来的变化。系统会用快照图片在多任务UI中去展示你app的状态。
如果你在进入到后台时做出了一些视图上的改变,你可以在你的主视图上调用 snapshotViewAfterScreenUpdates:
方法去强制这些变化被渲染。调用setNeedsDisplay方法对快照的视图是没有用的,因为快照在下一次绘图前就就发生了,因此在渲染时应避免任何改变。调用snapshotViewAfterScreenUpdates:方法并且返回YES 强制更新快照。

减少你的内存台面空间

任何一个app,在进入后台时应该释放尽可能的内存。系统在同一时间试着去保存尽可量的多的app,并且保存他们的内存,但是当低内存状态发生时,它将会杀死那些挂起的app去释放内存。在后台期间占用了很大内存的App将成为系统第一个杀死的对象。
简单说来,你的app应该移除所有强引用的对象在它不被需要的时候。移除强引用是给编译器权利去释放掉这个对象,因此相应的内存也会被释放。然而如果你想要存储一些对象去提高性能,你可以在进入到后台后将这些引用移除。
一些你可以移除的对象的例子:
1. 你创建的Image对象。(一些return image的方法创建的image对象会被系统自动清除。更多内容请参阅 UIImage Class Reference.
2. 你可以从硬盘上重新加载的大的媒体文件和数据文件。
3. 任何你的app现在不需要的并且过后非常容易重新创建的对象。
为了帮助减少你的app的内存,系统会自动清除一些分配给你的app的数据当你的app进入到后台时。
4. 系统会把所有核心动画的backing store 清除掉。这并不是吧app的layer从内存里移除,也没有改变layer的属性。它只是阻止那些layer的content展示在屏幕上,虽然在后台这件事并不会发生。
5. 移除了系统引用的缓存图片。
6. 移除了一系统处理的数据缓存的强引用。

处理App状态改变的策略相关推荐

  1. 魔坊APP项目-23-种植园,宠物和种植物的状态改变、宠物的状态改动

    种植园 宠物和种植物的状态改变 1. 宠物的状态改动 2. 种植物的状态改动 3. 道具的使用 宠物的状态改动 因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们提前在mysql中进行配置 ...

  2. 图解Java设计模式学习笔记——行为型模式(模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式)

    一.模板方法模式(模板模式)--钩子方法 1.需求-豆浆制作问题 编写制作豆浆的程序,说明如下: 制作豆浆的流程选材-->添加配料-->浸泡-->放到豆浆机打碎. 通过添加不同的配料 ...

  3. 策略模式和工厂模式的区别_java设计模式之状态模式,策略模式孪生兄弟

    状态模式 状态模式(State Pattern)中,类的行为是基于它的状态改变的,状态之间的切换,在状态A执行完毕后自己控制状态指向状态B,状态模式是不停的切换状态执行.这种类型的设计模式属于行为型模 ...

  4. java状态模式和策略模式_Java状态和策略设计模式之间的差异

    java状态模式和策略模式 为了在Core Java应用程序中正确使用状态和策略设计模式,对于Java开发人员清楚地了解它们之间的区别很重要. 尽管状态和策略设计模式的结构相似,并且都基于开放式封闭设 ...

  5. 状态模式和策略模式的区别

    区别主要体现在行为上,而不是结构上,所以,看时序图就能很好的看出两者的区别. 状态模式 看1.4,状态B是状态A创建的,也就是由系统本身控制的.调用者不能直接指定或改变系统的状态转移 所以,状态是系统 ...

  6. android sim卡状态改变广播,android监控SIM卡状态的广播示例代码

    /* 监听sim状态改变的广播,返回sim卡的状态, 有效或者无效. 双卡中只要有一张卡的状态有效即返回状态为有效,两张卡都无效则返回无效. */ import android.app.Service ...

  7. 判断手机是否弹出键盘,改变了手机页面高度,对应inputStatus的状态改变相关控件的大小和位置

    inputResizeBodyHeight() {// 判断手机是否弹出键盘,改变了手机页面高度,对应inputStatus的状态改变相关控件的大小和位置var oldHeight =document ...

  8. [react] 在react中怎样改变组件状态,以及状态改变的过程是什么?

    [react] 在react中怎样改变组件状态,以及状态改变的过程是什么? 使用this.setState改变组件的状态 改变的过程中,React Fiber Reconciler遍历了整个Fiber ...

  9. 设置超链接在各种状态改变的样式颜色

    设置超链接在各种状态改变的样式颜色,在html的<head>标签下面添加下面的样式,可以自己根据需要修改样式. <style> a:link {color:blue;} a:v ...

  10. 双卡手机,sim卡状态改变测试

    在sim卡状态改变的广播接收着中: String iccState = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE); iccState表示当 ...

最新文章

  1. 旷视MegEngine数据加载与处理
  2. js实现审批流_JavaScript实现审核流程状态的动态显示进度条
  3. linux网络命名空间详解,『中级篇』 Linux网络命名空间(25)
  4. 计算机网络-基本概念(7)【网络层】-多协议标记交换MPLS
  5. leetcode 637. Average of Levels in Binary Tree | 637. 二叉树的层平均值(Java)
  6. layui upload 后台获取不到值
  7. python 经典100例(1-20)
  8. 证券基金行业IT运维“远景”如何应对?
  9. 马云曾卖鲜花,柳传志卖冰箱!摆摊吧,程序员!
  10. 设备 esp32_低功耗ESP32手持式袖珍显示屏
  11. Win7家庭版启用Administrator账户
  12. ftpphp_PHP实现ftp上传文件示例
  13. 读取xml数据装配到字典中之应用场景
  14. pycharm添加conda虚拟环境
  15. LQR控制算法的浅析
  16. 花了三天三夜才收集整理出来的经典 SQL 数据库笔试题及答案
  17. bzoj5369 [Pkusc2018]最大前缀和
  18. 旋转编码器EC11_支持长按、短按、双击、顺时针逆时针
  19. Python图形处理
  20. AMP—Rover移植

热门文章

  1. Linq的Distinct太不给力了
  2. 7种JS脚本分页代码 showPages v1.0
  3. 博弈论——选举/投票(voting)
  4. QML Item定位器 Anchor
  5. SQL不能以实例名连接问题处理
  6. c语言扫掠数组,科学网—COMSOL 个人笔记 - 刘铨鸿的博文
  7. VS2013 + Qt 提示 There‘s no Qt version assigned to this project for platform Win32
  8. OpenCasCade将鼠标点映射到OCC三维视图中的三维点(鼠标点转换为OCC三维坐标)
  9. 安卓rom制作教程_【教程】安卓手机刷入第三方ROM通用教程
  10. JAVA 实现生命游戏