最近在阅读 Android 开发艺术探索,提出重点做个笔记

生命周期大致有两部分,一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期。所谓典型情况下的生命周期,是指在有用户参与的情况下,Activity 所经过的生命周期的改变;而异常情况下的生命周期是指 Activity 被系统回收或者由于当前设备的Configuration 发生改变从而导致被销毁重建,异常情况下的生命周期的关注点和典型情况下略有不同。

典型情况下的生命周期分析

在正常情况下,Activity 会经历如下生命周期。

(1) onCreate : 表示 Activity 正在被创建,这是生命周期的第一个方法,在这个方法中,我们可以做一些初始化工作,比如调试 setContentView 去加载界面布局资源、初始化 Activity 所需数据

(2) onRestart : 表示 Activity 正在重新启动,一般情况下,当当前 Activity 从不可见重新变为可见状态时,onRestart 就会被调用。这种情形一般是用户行为所导致的,比如用户按 Home 键切换到桌面或者用户打开了一个新的 Activity,这时当前的 Activity 就会暂停,也就是 onPause 和 onStop 被执行了,接着用户又回到了这个 Activity , 就会出现这种情况。

(3) onStart : 表示 Activity 正在被启动,即将开始,这时 Activity 已经可见了,但是还没有出现在前台,还无法和用户交互。这个时候其实可以理解为 Activity 已经显示出来了, 但是我们还看不到。

(4) onResume : 表示 Activity 已经可见了,并且出现在前台并开始活动。要注意这个和 onStart 的对比,onStart 和 onResume 都表示Activity 己经可见,但是 onStart 的时候 Activity 还在后台,onResume 的时候 Activity 才显示到前台。

(5) onPause : 表示 Activity 正在停止,正常情况下,紧接着 onStop 就会被调用。在特殊情况下,如果这个时候快速地再回到当前 Activity , 那么 onResume 会被调用。笔者的理解是,这种情况属于极端情况,用户操作很难重现这一场景。此时可以做一些存储数据、 停止动画等工作,但是注意不能太耗时,因为这会影响到新 Activity 的显示,onPause 必须先执行完,新 Activity 的 onResume 才会执行。

(6) onStop : 表示 Activity 即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。

(7) onDestroy : 表示 Activity 即将被销毁,这是 Activity 生命周期中的最后一个回调,在这里我们可以做一些回收工作和最终的资源释放。

正常情况下,Activity 的常用生命周期就只有上面 7 个,下图更详细地描述了 Activity 各种生命周期的切换过程。

异常情况下的生命周期分析

资源相关的系统配置发生改变导致Activity被杀死并重新创建

理解这个问题,我们首先要对系统的资源加载机制有一定了解,这里不详细分析系统 的资源加载机制,只是简单说明一下。拿最简单的图片来说,当我们把一张图片放在 drawable 目录后,就可以通过 Resources 去获取这张图片。同时为了兼容不同的设备,我们 可能还需要在其他一些目录放置不同的图片,比如drawable-mdpi、drawable-hdpi、 drawable-land 等。这样,当应用程序启动时,系统就会根据当前设备的情况去加载合适的 Resources 资源,比如说横屏手机和竖屏手机会拿到两张不同的图片(设定了 landscape或者 portrait 状态下的图片)。比如说当前 Activity 处于竖屏状态,如果突然旋转屏幕,由于 系统配置发生了改变,在默认情况下,Activity 就会被销毁并且重新创建,当然我们也可以 阻止系统重新创建我们的 Activity 。

在默认情况下,如果我们的 Activity 不做特殊处理,那么当系统配置发生改变后, Activity就会被销毁并重新创建,其生命周期如图所示。

当系统配置发生改变后,Activity 会被销毁,其 onPause、onStop、onDestroy 均会被调用,同时由于 Activity 是在异常情况下终止的,系统会调用 onSavelnstanceState 来保存当前 Activity 的状态。这个方法的调用时机是在 onStop 之前,它和 onPause 没有既定的时序关 系,它既可能在 onPause 之前调用,也可能在 onPause 之后调用。需要强调的一点是,这 个方法只会出现在 Activity 被异常终止的情况下,正常情况下系统不会回调这个方法。当 Activity 被重新创建后,系统会调用onRestorelnstanceState , 并且把Activity销毁时 onSavelnstanceState 方法所保存的 Bundle 对象作为参数同时传递给 onRestoreInstanceState 和 onCreate 方法。因此,我们可以通过 onRestorelnstanceState 和 onCreate 方法来判断 Activity 是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState 的调用时机在 onStart 之后。

同时,我们要知道,在 onSaveInstanceState 和 onRestoreInstanceState 方法中,系统自动 为我们做了一定的恢复工作。当 Activity 在异常情况下需要重新创建时,系统会默认为我们保存当前 Activity 的视图结构,并且在 Activity 重启后为我们恢复这些数据,比如文本框 中用户输入的数据、ListView 滚动的位置等,这些 View 相关的状态系统都能够默认为我们恢复。

这里需要注意的是,系统只有在 Activity 异常终止的时候才会调用 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数据,其他情况下不会触发这个过程。

资源内存不足导致低优先级的Activity被杀死

这种情况我们不好模拟,但是其数据存储和恢复过程和情况1完全—致。这里我们描述一下 Activity 的优先级情况。Activity 按照优先级从高到低,可以分为如下三种:

(1) 前台 Activity 正在和用户交互的 Activity ,优先级最髙。

(2) 可见但非前台 Activity 比如 Activity 中弹出了一个对话框,导致Activity可见,但是位于后台无法和用户直接交互。

(3) 后台 Activity 已经被暂停的 Activity ,比如执行了 onStop ,优先级最低。

当系统内存不足时,系统就会按照上述优先级去杀死目标 Activity 所在的进程,并在后续通过 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱 离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入 Service 中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。

上面分析了系统的数据存储和恢复机制,我们知道,当系统配置发生改变后,Activity 会被重新创建,那么有没有办法不重新创建呢?答案是有的,接下来我们就来分析这个问 题。系统配置中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建 Activity, 可以给 Activity 指定 configChanges 属性。比如不想让Activity在屏幕旋转的时候重新创建, 就可以给 configChanges 属性添加orientation 这个值,如下所示。

1
android:configChanges="orientation"

如果我们想指定多个值,可以用“ | ”连接起来,比如android:configChanges= “orientation|keyboardHidden”。系统配置中所含的值是非常多的,下面介绍每个值的含义,如下表所示。

含义
mcc SIM卡唯一标识IMSI (国际移动用户识别码)中的国家代码,由三位数字组成,中国为460。此项 标识mcc代码发生了改变
mnc SIM卡唯一标识IMSI (闽际移动用户识别码)中的运营商代码,出两位数字组成,中围移动TD系 统为00,中国联通为01,中闺电信为03。此项标识mnc发生改变
locale 设备的本地位罝发生了改变,一般指切换了系统语言
touchscreen 触摸屏发生了改变,这个很费解,正常情况下无法发生,可以忽略它
keyboard 键盘类型发生了改变,比如用户使用了外插键盘
keyboardHidden 键盘的可访问性发生了改变,比如用户调出了键盘
navigation 系统导航方式发生了改变,比如采用了轨迹球导航,这个有点费解,很难发生,可以忽略它
screenLayout 屏幕布局发生了改变,很可能足用户激活了另外一个显示设备
fontScale 系统字体缩放比例发生了改变,比如用户选择了一个新字号
uiMode 用户界面模式发生了改变,比如是否开启了夜间模式(API 8新添加)
orientation 屏幕方向发生了改变,这个是最常用的,比如旋转了手机屏幕
screenSize 当屏幕的尺寸信息发生了改变,当旋转设备屏蓓时,屏褅尺寸会发生变化,这个选项比较特殊,它 screenSize 和编译选项有关,当编译选项中的minSdkVersion和targetSdkVersion均低于13时,此选项不会导致Activity重启,否则会导致Activity重启(API 13新添加)
smallestScreenSize 设备的物理屏幕尺寸发生改变,这个项目和屏幕的方向没关系.仅仅表示在实际的物理屏幕的尺寸 改变的时候发生,比如用户切换到了外部的显示设备,这个选项和screenSize—样,当编译选项中的 minSdkVersion和targetSdkVersion均低于13时,此选项不会导致Activity重启,否则会导致Activity重启(API 13新添加)
layoutDirection 当布局方向发生变化,这个属性用的比较少,正常情况下无须修改布同的layoutDirection属性(API 17新添加)

从上表可以知道,如果我们没有在 Activity 的 configChanges 属性中指定该选项的话, 当配置发生改变后就会导致 Activity 重新创建。上面表格中的项目很多,但是我们常用的 只有 locale、orientation 和 keyboardHidden 这三个选项,其他很少使用。

另外需要注意的是,自从Android 3.2(API 13),在设置 Activity 的 android:configChanges=“orientation|keyboardHidden” 后,还是一样会重新调用各个生命周期的。因为 screen size也开始跟着设备的横竖切换而改变。所以,在 AndroidManifest.xml 里设置的 MiniSdkVersion 和 TargetSdkVersion 属性大于等于13的情况下,如果你想阻止程序在运行时重新加载 Activity,除了设置 orientation,你还必须设置 ScreenSize 。

总结

Activity生命周期在项目中的具体使用

生命周期 建议
onCreate 一些变量的初始化,资源的加载,初始化控件以及事件的绑定等
onStart 检查某些必须的系统特性是否可用,比如网络是否在连接, GPS是否打开等类似的功能
onRestart 一般用来恢复用户数据
onResume 刷新操作
onPause 该方法中需要持久化用户数据、停止动画,暂停正在播放的视频等不太耗时的操作
onStop 可以做一些稍微重量级的回收工作,同样不能太耗时
onDestroy 可以做一些回收工作和最终的资源释放,比如常用 EventBus 的取消注册