重温 Activity 分析一:生命周期

文章目录
  1. 1. 简介生命周期
  2. 2. 归纳生命周期
  3. 3. 深入生命周期
  4. 4. 参考文献

If you can’t fly then run, can’t run then walk, can’t walk then crawl, just keep moving forward.

记得当初学习 Android 开发的时候,接触的四大组件的第一个就是 Activity,这么久过去了,有时候会问自己,你真得掌握了 Activity 吗?回答是没有。”勿以浮沙筑高台“,借此机会,好好反刍整理下,姑且先谈一谈 Activity 的生命周期。

简介生命周期

月有阴晴圆缺,人有生老病死,自然界中无时无刻不在发生着各种“生命”的诞生和湮灭。如果将程序代码看作是有生命的话,那么我们这里的 Activity 也有着自己的一套生命周期。话不多说,先上实例,ActivityLifeCycleDemo 地址: ActivityLifeCycleDemo

主要看该部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private Intent mIntent;
private Button mBtnNormal, mBtnDialog;
public static final String TAG = "MAIN_ACTIVITY";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Log.d(TAG, "onCreate");

setContentView(R.layout.activity_main);

mBtnNormal = (Button) findViewById(R.id.btn_start_normal);
mBtnDialog = (Button) findViewById(R.id.btn_start_dialog);

mBtnNormal.setOnClickListener(this);
mBtnDialog.setOnClickListener(this);
}

@Override
public void onClick(View v) {

switch (v.getId()) {
case R.id.btn_start_normal:
mIntent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(mIntent);
break;
case R.id.btn_start_dialog:
mIntent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(mIntent);
break;
default:
break;
}
}

@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}

@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}

@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}

@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}

@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}

@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}

}

运行程序,效果如下图:

查看 Android Monitor 中的 logcat 如下:

1
2
3
05-21 05:19:22.710 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onCreate
05-21 05:19:22.715 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onStart
05-21 05:19:22.715 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onResume

显然,当 MainActivity 第一次被创建的时候,会依次执行onCreate()—>onStart()—>onResume()方法。接着,点击第一个按钮,启动 NormalActivity,如图所示:

此时 logcat 中打印:

1
2
05-21 05:26:49.128 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onPause
05-21 05:26:49.652 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onStop

原因是 NormalActivity 将 MainActivity 完全遮挡住了,因此依次执行onPause()—>onStop()方法。然后,按下 Back 键返回 MainActivity,logcat 中打印:

1
2
3
05-21 05:34:20.779 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onRestart
05-21 05:34:20.779 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onStart
05-21 05:34:20.779 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onResume

之前 MainActivity 已经进入了停止状态,此时,并没有重新创建。因此,这时候执行onRestart()方法,而不执行onCreate()方法。即依次执行onRestart()—>onStart()—>onResume()方法。再点击第二个按钮,启动 DialogActivity,如图所示:

logcat 中打印出:

1
05-21 05:41:59.861 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onPause

由于 DialogActivity 并没有完全遮挡住 MainActivity,MainActivity 只是进入了暂停状态,没有进入停止状态。故只执行了onPause()方法,未执行onStop()方法。自然,按下 Back 键返回 MainActivity 只有 onResume() 方法执行,如 logcat 中所示:

1
05-21 05:49:33.079 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onResume

最后,在 MainActivity 界面按下 Back 键退出程序,logcat 中所示如下:

1
2
3
05-21 05:51:19.488 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onPause
05-21 05:51:19.491 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onStop
05-21 05:51:19.491 17557-17557/com.iamasoldier6.activitylifecycledemo D/MAIN_ACTIVITY: onDestroy

依次会执行onPause()—>onStop()—>onDestroy()方法,最终销毁 MainActivity。

如此,便完整地走了一遍 Activity 的生命周期。

归纳生命周期

Activity 的生命周期通过实现上述的回调方法来进行管理。Activity 的生命周期会直接受到其本身与其他 Activity、其本身的任务及返回栈关联性的影响。Activity 基本上以三种状态存在:

运行

此 Activity 位于屏幕前台并具有用户焦点。

暂停

另一个 Activity B 位于屏幕前台,且具有用户焦点,但此 Activity 仍然可见。也就是说,另一个 Activity B 显示在此 Activity 上方,并且该 Activity B 部分透明或未覆盖整个屏幕,比如前文提及的对话框形式的活动 DialogActivity。暂停的 Activity 处于完全活动状态,Activity 对象保留在内存中,其保留了所有状态和成员信息,并与窗口管理器保持连接。注意,在内存极度不足的情况下,可能会被系统终止

停止

该 Activity 被另一个 Activity 完全遮盖,该 Activity 目前位于“后台”。已停止的 Activity 同样仍处于活动状态,Activity 对象保存在内存中,其保留了所有状态和成员信息,但与窗口管理器连接。注意,其对用户不再可见,在别的地方需要内存时,可能会被系统终止

若 Activity 处于暂停或停止状态,系统可通过要求其结束,调用其 finish() 方法或直接终止其进程,将其从内存中删除。将其结束或终止后,再次打开此 Activity 时,必须要重建。

下面作出归纳整理和小结,参见 Android 官网自己整理的图:

归纳及总结如下:

完整生命周期

onCreate()~onDestroy() 之间。Activity 在 onCreate() 中执行“全局”状态设置(如定义布局),完成各种初始化操作;在 onDestroy() 中释放所有其余资源。例如,Activity 有一个在后台运行的线程,用于从网络上下载数据,可能会在 onCreate() 中创建线程,然后在 onDestroy() 中停止线程。

可见生命周期

onStart()~onStop()之间。用户可以在屏幕上看到 Activity 并与其交互,开发者可以在这两个方法之间保留向用户显示 Activity 所需的资源。例如,可以在 onStart() 中注册一个 BroadcastReceiver 以监控影响 UI 的变化,并在用户无法看到显示的内容时在 onStop() 中将其取消注册。

前台生命周期

onResume()~onPause()之间。Activity 位于屏幕上的所有其他 Activity 之前,并具有用户输入焦点。Activity 可以频繁地转入和转出前台。例如,当设备转入休眠状态或出现对话框时,系统会调用 onPause()。注意,由于此状态可能经常发生转变,故这两个方法中应采用适度轻量级的算法,避免因为转变速度慢让用户等待。

细分来看每个回调方法如下表格所示:

回调方法 说明
onCreate() Activity 正在被创建。首次创建 Activity 时调用,在该方法中执行所有正常的静态设置,如创建视图、初始化所需数据等。系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,前提是捕获了该状态。始终后接 onStart()
onRestart() Activity 正在重新启动。在 Activity 已停止并即将再次启动前调用,即一般情况下,当前 Activity 从不可见重新变为可见状态时调用。比如,用户按 Home 键切换到桌面或者用户打开一个新的 Activity,当前的 Activity 就会暂停,即执行了 onPause() 和 onStop() 方法,接着用户重新回到这个 Activity,就会出现该情况。始终后接 onStart()
onStart() Activity 正在被启动。在 Activity 即将对用户可见之前调用,其实,这时候 Activity 已经可见了,但是还没有出现在前台,还无法和用户交互。若 Activity 转入前台,则后接 onResume();若 Activity 转入隐藏状态,则后接 onStop()
onResume() Activity 已经可见,出现在前台并开始活动。在 Activity 即将开始与用户进行交互之前调用,此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。注意,虽然 onStart() 和 onResume() 都表示 Activity 已经可见,但是 onStart() 时 Activity 还在后台,onResume() 时才显示到前台。始终后接 onPause()
onPause() Activity 正在暂停。当系统即将开始另一个 Activity 时调用,该方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容。注意不能太耗时,应该非常迅速地执行所需操作。若 Activity 返回前台,则后接 onResume(),但该操作属于极端情况,用户操作很难重现;若 Activity 转入对用户不可见状态,则后接 onStop()
onStop() Activity 即将停止。在 Activity 对用户不再可见时调用。若 Activity 被销毁,或另一个 Activity (一个现有 Activity 或新 Acitivity) 继续执行并将其覆盖,会发生该情况。这里,可以做一些稍微重量级的回收工作,同样不能太耗时。若 Activity 恢复与用户的交互,则后接 onRestart();若 Activity 被销毁,则后接 onDestroy()
onDestroy() Activity 即将被销毁。在 Activity 被销毁前调用,这是 Activity 将收到的最后调用。当 Activity 结束(对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能调用它。

深入生命周期

前文分析了典型情况下 Activity 的生命周期,接着来探讨下 Activity 在异常情况下的生命周期。我们知道,Activity 除了受用户操作所导致的正常的生命周期方法调度,还有一些异常情况,比如当资源相关的系统配置发生改变以及系统内存不足时,Activity 就可能被杀死。

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

我们知道,手机在横屏和竖屏时会拿到资源文件中两张不同的图片(设定了 landscape 或 portrait 时的图片)。若当前 Activity 处于竖屏状态,突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity 就会被销毁并重新创建,我们可以采取相应的处理方法阻止系统重新创建。

默认情况时,Activity 不做特殊处理,则当系统配置发生改变后,Activity 就会被销毁并重新创建,如图所示:

当系统配置改变后,Activity 会被销毁,其 onPause()、onStop() 和 onDestroy() 都被调用,同时,由于 Activity 是在异常情况下终止的,系统会调用 onSaveInstanceState() 来保存当前 Activity 的状态。注意,这个方法的调用时机是在 onStop() 之前,和 onPause() 没有特定的时序关系,既可能在 onPause() 之前调用,也可能在 onPause() 之后调用。更需要强调的是,这个方法只会出现在 Activity 被异常终止的情况下,正常情况下系统不会回调这个方法。当 Activity 被重新创建后,系统会调用 onRestoreInstanceState(),此时,Activity 销毁时 onSaveInstanceState() 方法所保存的 Bundle 对象作为参数同时传递给 onRestoreInstanceState() 和 onCreate() 方法。因此,可以通过 onRestoreInstanceState() 和 onCreate() 方法判断 Activity 是否被重建,若被重建了,就可以取出之前保存的数据并恢复。注意,onRestoreInstanceState() 的调用在 onStart() 之后

系统只在 Activity 异常终止的时候才会调用 onSaveInstanceState() 和 onRestoreInstanceState() 来存储和恢复数据,其他情况不会触发这个过程

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

其数据存储和恢复过程与情况 I 一致。Activity 优先级从高到低,分为如下三种:

(1) 前台 Activity ——— 正在和用户交互的 Activity,优先级最高;

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

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

若系统内存不足时,系统会按照上述优先级去杀死目标 Activity 所在的进程,然后在后续通过 onSaveInstanceState() 和 onRestoreInstanceState() 来存储和恢复数据。

实际上,系统配置中有很多内容,若某项内容发生改变后,不想系统重新创建 Activity,可以给 Activity 指定 configChanges 属性。

至此,关于 Activity 的生命周期归纳分析完毕,下一篇是对 Activity 的启动模式的归纳分析。

本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。

1
Email: [email protected] / WeChat: Wolverine623

您也可以关注我个人的微信公众号码农六哥第一时间获得博客的更新通知,或后台留言与我交流

参考文献

1.https://developer.android.google.cn/guide/components/activities.html?hl=zh-cn

2.第一行代码

3.Android 开发艺术探索