Activity的生命周期
1 android
任务栈/Task
Android
中的Activity
是可以层叠的。每启动一个新的Activity
,就会覆盖在原Activity
之上,然后点击Back
键会销毁最上面的Activity
,下面的Activity
就会重新显示出来。
Android
是使用任务(task
)来管理Activity
的,一个任务就是一组存放在栈里的Activity
的集合,这个栈也被称作返回栈(back stack
)。栈是一种后进先出的数据结构,在默认情况 下,每当我们启动了一个新的Activity
,它就会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back
键或调用finish()
方法去销毁一个Activity
时,处于栈顶的Activity
就会出栈,前一个入栈的Activity
就会重新处于栈顶的位置。系统总是会显示处于栈顶的Activity
给用户。
在退出应用程序时,必须把所有的任务栈中所有的Activity
清除出栈,任务栈才会被销毁。当然任务栈也可以移动到后台,并且保留了每一个Activity
的状态,可以有序的给用户列出它们的任务,同时也不会丢失Activity
的状态信息。
下图展示了返回栈是如何管理Activity
入栈出栈操作的:
2 Activity
状态
每个Activity
在其生命周期中最多可能会有4
种状态。
- 运行状态:当一个
Activity
位于返回栈的栈顶时,Activity
就处于运行状态。 系统最不愿意回收的就是处于运行状态的Activity
,因为这会带来非常差的用户体验。 - 暂停状态:当一个
Activity
不再处于栈顶位置,但仍然可见时,Activity
就进入了暂停状态。 既然Activity
已经不在栈顶了,怎么会可见呢?这是因为并不是每一个Activity
都会占满整个屏幕,比如对话框形式的Activity
只会占用屏幕中间的部分区域。处于暂停状态的Activity
仍然是完全存活着的,系统也不愿意回收这种Activity
(因为它还是可见的,回收可见的东西都会在用户体验方面有不好的影响),只有在内存极低的情况下,系统才会去考虑回收这种Activity
。 - 停止状态:当一个
Activity
不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。 系统仍然会为这种Activity
保存相应的状态和成员变量,但是这并不是完全可靠的,当其他地方需要内存时,处于停止状态的Activity
有可能会被系统回收。 - 销毁状态:一个
Activity
从返回栈中移除后就变成了销毁状态。 系统最倾向于回收处于这种状态的Activity
,以保证手机的内存充足。
3 Activity
的生存期
Activity
类中定义了7
个回调方法,覆盖了Activity
生命周期的每一个环节:
onCreate
:表示Activity
正在被创建,这是生命周期的第一个方法。 在这个方法中,可以做一些初始化工作,比如调用setContentView
去加载页面布局资源,初始化Activity
所需数据等。onStart
:表示Activity
正在被启动,即将开始,这时Activity
已经可见,但是还没有出现在前台,还无法和用户交互。 这个时候其实可以理解为Activity
已经显示出来了,但是我们还看不到。onResume
:表示Activity
已经可见,出现在前台并且开始活动。 此时的Activity
一定位于返回栈的栈顶,并且处于运行状态。onStart
和onResume
都表示Activity
已经可见,但是onStart
的时候Activity
还在后台,onResume
的时候Activity
才显示到前台。onPause
:表示Activity
正在停下,正常情况下,紧接着onStop
就会被调用。 在特殊情况下,如果这个时候快速的回到当前Activity
,那么onResume
会被调用,这种属于极端的情况,比较难出现。在此方法中,可以做一些存储数据、停止动画等工作,但是不能太耗时,因为会影响到新的Activity
的显示,onPause
必须先执行完,新的Acitivty
的onResume
才会执行。onStop
:表示Activity
即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。 它和onPause()
方法的主要区别在于,如果启动的新Activity
是一个对话框式的Activity
,那么onPause()
方法会得到执行,而onStop()
方法并不会执行。onDestory
:表示Activity
即将被销毁,这是Activity
生命周期的最后一个回调,在这个方法中,可以做一些回收工作和最终的资源释放。onRestart
:表示Activity
正在重新启动。一般情况下,当当前的Activity
从不可见重新变成可见状态时,onRestart
就会被调用。 这种情形一般时用户行为所致,比如用户按Home
键切换到桌面或者用户打开了一个新的Activity
,这个时候当前的Activity
就会暂停,也就是onPause
和onStop
被执行了,该用户又回到这个Activity
,就会出现这种情况。
Android
官方提供了一张Activity
生命周期的示意图,如图所示:
以上7
个方法中除了onRestart()
方法,其他都是两两相对的,从而又可以将Activity
分为以 下3
种生存期:
- 完整生存期:
Activity
在onCreate()
方法和onDestroy()
方法之间所经历的就是完整生存期。 一般情况下,一个Activity
会在onCreate()
方法中完成各种初始化操作,而在onDestroy()
方法中完成释放内存的操作。 - 可见生存期:
Activity
在onStart()
方法和onStop()
方法之间所经历的就是可见生期。 在可见生存期内,Activity
对于用户总是可见的,即便有可能无法和用户进行交互。可以通过这两个方法合理地管理那些对用户可见的资源。比如在onStart()
方法中对资源进行加载,而在onStop()
方法中对资源进行释放,从而保证处于停止状态的Activity
不会占用过多内存。 - 前台生存期:
Activity
在onResume()
方法和onPause()
方法之间所经历的就是前台生存期。 在前台生存期内,Activity
总是处于运行状态,此时的Activity
是可以和用户进行交互的,我们平时看到和接触最多的就是这个状态下的Activity
。
从整个生命周期来说,onCreate
和onDestory
是配对的,分别标识着Activity
的创建和销毁,并且只可能有一次调用;从Activity
是否可见来说,onStart
和onStop
是配对的,这两个方法可能被调用多次;从Activity
是否在前台来说,onResume
和onPause
是配对的,这两个方法可能被调用多次。
4 典型情况下的生命周期分析
情况1
. 从某个普通的Activity
——MainActivity
,跳转到另一个普通的Activity
——NormalActivity
,MainActivity
的生命周期:
- 启动
MainActivity
:onCreate
->onStart
->onResume
- 跳转到
NormalActivity
:onPause
->onStop
startNormalActivity.setOnClickListener {
val intent = Intent(this, NormalActivity::class.java)
startActivity(intent)
}
- 在
NormalActivity
中返回到MainActivity
:onRestart
->onStart
->onResume
- 点击返回,关闭
MainActivity
:onPause -> onStop -> onDestory
情况2
. 从某个普通的Activity
——MainActivity
,跳转到另一个对话框式的Activity
——DialogActivity
或透明主题的Activity
,MainActivity
的生命周期:
在AndroidManifest.xml
中配置DialogActivity
的主题:
<activity
android:name=".DialogActivity"
android:exported="true"
android:theme="@style/Theme.AppCompat.Dialog" />
- 启动
MainActivity
:onCreate
->onStart
->onResume
- 跳转到
DialogActivity
:onPause
- 点击返回,关闭
DialogActivity
:onResume
- 点击返回,关闭
MainActivity
:onPause -> onStop -> onDestory
另外,如果Dialog
是直接是通过WindowManager.addView
显示的(没有经过AMS
),是不会对生命周期有任何影响的。
情况3
:假设当前为Activity A
,如果这时用户打开一个新的Activity B
,那么B.onResume
和A.onPause
哪个先执行
- 启动
Activity A
:A.onCreate
->A.onStart
->A.onResume
- 跳转到
Activity B
:A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop
- 点击返回到
Activity A
:B.onPause -> A.onRestart -> A.onStart -> A.onResume -> B.onStop -> B.onDesotry
情况4
:点击Home
键或者锁屏键,再次回到应用:
- 启动:
onCreate
->onStart
->onResume
- 点击
Home
键:onPause -> onStop
- 回到应用:
onRestart -> onStart -> onResume
5 异常情况下的生命周期分析
异常情况下的生命周期是指Activity
被系统回收或者由于当前设备的Configuration
发生改变从而导致Activity
被销毁重建。
5.1 资源相关的系统配置发生改变导致Activity
被杀死并重新创建
系统的资源加载机制,比如说图片加载,一张图片放在drawable
目录后,就可以通过Resource
去获取这张图片,同时为了兼容不同的设备,可能还需要在其他一些目录下放置不同的图片,比如drawable-mdpi
、drawable-hdpi
、drawable-land
等,这样,当应用程序启动时,系统就会根据当前设备的情况去加载合适的Resource
资源,比如说横屏手机和竖屏手机会拿到两张不同的图片(设定了landscape
或portrait
状态下的图片)。比如说当前Activity
处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity
就会销毁并且重建,当然,也可以阻止系统重新创建Activity
。
在默认情况下,如果Activity
不做特殊处理,那么当系统配置发生改变后,Activity
就会被销毁并且重新创建,其生命周期如下所示:
当系统配置发生改变后,Activity
会被销毁,其onPause
、onStop
、onDestory
均会被调用,同时,**由于Activity
是在异常情况下终止的,系统会调用onSaveInstanceState
来保存当前Activity
的状态。**这个方法的调用时机是在onStop
之前,它和onPause
没有既定的时序关系,既可能在onPause
之前调用,也可能在onPause
之后调用。需要强调的一点是,这个方法只会出现在Activity
被异常终止的情况下,正常情况下,系统不会回调这个方法。
当Activity
被重新创建后,系统会调用onRestoreInstanceState
,并且把Activity
销毁时onSaveInstanceState
方法所保存的Bundle
对象作为参数同时传递给onRestoreInstanceState
和onCreate
方法。因此,可以通过onRestoreInstanceState
和onCreate
方法来判断Activity
是否被重建了,如果被重建了,那么就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState
的调用时机在onStart
之后。
在onSaveInstanceState
和onRestoreInstanceState
方法中,系统会自动做一定的恢复工作。当Activity
在异常情况下需要重新创建时,系统会默认为我们保存当前Activity
的视图结构,并且在Activity
重启后会恢复这些数据,比如文本框中用户输入的数据、ListView
滚动的位置等,这些View
相关的状态系统都能够默认恢复。具体针对某一个特定的View
系统能恢复哪些数据,可以查看View
的源码,和Activity
一样,每个View
都有onSaveInstanceState
和onRestoreInstanceState
这两个方法。
关于保存和恢复View
层次结构:首先Activity
被意外终止时,Activity
会调用onSaveInstanceState
去保存数据,然后Activity
会委托Window
去保存数据,接着Window
再委托它上面的顶级容器去保存数据。顶层容器是一个ViewGroup
,一般来说他可能是DecorView
。最后顶层容器再去一一通知它的子元素去保存数据,这样整个数据保存过程就完成了。这一种典型的委托思想,上层委托下层,父容器委托子元素去处理一件事情。
onRestoreInstanceState
和onCreate
的区别是:onSaveInstanceState
不一定会被调用,因为它只有在Activity
被回收了才会调用。onRestoreInstanceState
一旦被调用,其参数Bundle savedInstanceState
一定是有值的,不用额外的判断是否为空;但onCreate
不行,onCreate
如果是正常启动的话,其参数Bundle savedInstanceState
为null
,所以必须采用额外的判断。 这两个方法可以选择任意一个进行数据恢复,但是官方文档的建议是采用onRestoreInstanceState
去恢复数据。
// MainActivity: onPause:
// MainActivity: onSaveInstanceState:
// MainActivity: onStop:
// MainActivity: onDestroy:
Activity
被销毁以后调用了onSaveIntanceState
来保存数据,重新创建以后在onCreate
和onRestoreInstanceState
种都能够正确地恢复之前存储的数据。**针对onSaveInstanceState
方法还有一点需要说明,那就是系统只会在Activity
即将被销毁并且有机会重新显示的情况下才会去调用它。当Activity
正常销毁的时候,系统不会调用onSaveInstanceState
,因为被销毁的Activity
不可能再次被显示。**比如,对比一下旋转屏幕所造成的Activity
异常销毁,这个过程和正常停止Activity
是不一样的,因为旋转屏幕后,Activity
被销毁的同时会立刻创建新的Activity
实例,这个时候Activity
有机会再次立刻展示,所以系统要进行数据存储。可以简单地理解成,系统只在Activity
异常终止的时候才会调用onSaveInstanceState
和onRestoreInstanceState
来存储和恢复数据,其他情况不会触发这个过程。
onSaveInstanceState
和onResoreInstanceState
的相关流程:
以下是onSaveInstanceState
的相关源码:
public final class ActivityThread {
final void performStopActivity(IBinder token, boolean saveState, String reason) {
ActivityClientRecord r = mActivities.get(token);
performStopActivityInner(r, null, false, saveState, reason);
}
private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
boolean saveState, String reason) {
// One must first be paused before stopped...
performPauseActivityIfNeeded(r, reason);
// Next have the activity save its current state and managed dialogs...
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
callCallActivityOnSaveInstanceState(r);
}
}
}
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
if (r.isPersistable()) {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}
}
public class Instrumentation {
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
activity.performSaveInstanceState(outState);
}
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
PersistableBundle outPersistentState) {
activity.performSaveInstanceState(outState, outPersistentState);
}
}
public class Activity extends ContextThemeWrapper implements Window.Callback {
final void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
}
final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
}
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
onSaveInstanceState(outState);
}
}
以下时onResoreInstanceState
的源码:
public final class ActivityThread {
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!r.activity.mFinished) {
activity.performStart();
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
}
}
public class Instrumentation {
public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
activity.performRestoreInstanceState(savedInstanceState);
}
public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,
PersistableBundle persistentState) {
activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
}
public class Activity extends ContextThemeWrapper implements Window.Callback {
final void performRestoreInstanceState(Bundle savedInstanceState) {
onRestoreInstanceState(savedInstanceState);
restoreManagedDialogs(savedInstanceState);
}
final void performRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState) {
onRestoreInstanceState(savedInstanceState, persistentState);
if (savedInstanceState != null) {
restoreManagedDialogs(savedInstanceState);
}
}
}
onSaveInstanceState()
的调用时机:
- 从当前
Activity
启动一个新的Activity
时:onPause -> onSaveInstanceState -> onStop
- 屏幕方向切换(论竖屏切横屏还是横屏切竖屏都会调用)时:
onPause -> onSaveInstanceState -> onStop
- 按下
HOME
键时:onPause -> onSaveInstanceState -> onStop
- 按下电源按键(关闭屏幕显示)时:
onPause -> onSaveInstanceState -> onStop
- 从最近应用中选择运行其他的程序时:
onPause -> onSaveInstanceState -> onStop
onRestoreInstanceState
的调用时机:onRestoreInstanceState()
只有在Activity
确实是被系统回收,重新创建Activity
的情况下才会被调用
- 屏幕方向切换时,
Activity
生命周期如下:onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
- 被后台回收
按HOME
键返回桌面,又马上点击应用图标回到原来页面时不会被回收。
在一般情况下,横竖屏切换的完整生命周期:onCreate -> onStart ->onResume -> onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
当系统配置发生改变后,Activity
会被重新创建,如果不想系统重新创建Activity
,可以给Activity
指定configChanges
属性。比如不想让Activity
在屏幕旋转的时候重新创建,就可以给configChanges
属性添加orientation
这个值:android:configChanges="orientation"
orientation [ˌɔːriənˈteɪʃn] 方向;定向;适应;情况介绍
如果想指定多个值,可以用|
连接起来,比如android:configChanges="orientation|keyboardHidden"
。系统配置中所含有的羡慕是非常多的,以下是每个项目的含义:
从上图中可以知道,如果没有在Activity
的configChanges
属性中指定该选项的话,当配置发生改变后就会导致Activity
重新创建,常用的只有locale
、orientation
和keyboardHidden
这三个选项,其他很少使用。需要注意的是screenSize
和smallestScreenSize
,它们两个比较特殊,它们的行为和变异选项有关,但和运行环境无关。
不设置Activity
的androidconfigChanges
,或设置Activity
的androidconfigChanges="orientation"
,或设置Activity
的android:configChanges="orientation|keyboardHidden"
,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行一次。
配置android:configChanges="orientation|keyboardHidden|screenSize"
,才不会销毁Activity
,且只调用 onConfigurationChanged
方法。
由上面的日志可见,Activity
的确没有重新创建,并且没有调用onSaveInstanceState
和onRestoreInstanceState
来存储和恢复数据,取而代之的是系统调用了Activity
的onConfigurationChanged
方法,这个时候就可以做一下自己的特殊处理了。
5.2 资源内存不足导致低优先级的Activity
被杀死
Activity
按照优先级从高到低,可以分为以下3
种情况:
- 前台
Activity
— 正在和用户交互的Activity
,优先级最高 - 可见但非前台
Activity
— 比如Activity
中弹出一个对话框,导致Activity
可见但是位于后台无法和用户直接交互 - 后台
Activity
— 已经被暂停的Activity
,比如执行了onStop
,优先级最低
当系统内存不足时,系统会按照上述的优先级去杀死目标Activity
所在的进程,并在后续通过onSaveInstanceState
和onRestoreInstanceState
来存储和恢复数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件而独立运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入Service
中从而保证进程有一定的优先级,这样就不会轻易的被系统杀死。