Activity 异常生命周期
说道Activity的生命周期相信所有Android开发者都耳熟能详,但那些只是一般情况下,除此以外Activity还存在异常情况的生命周期。
造成异常情况的发生主要分为以下两种:1、用户操作导致。2、系统自愿发生改变及内存不足。
Activity状态和内存的关系
系统会在需要释放内存的时候杀死进程,一个进程是否会被系统杀死取决于当时的状态,而进程的状态往往取决于其Activity的状态。以下是几种常见的状态和其对应的情况:
Likelihood of being killed | Process state | Activity state |
---|---|---|
Least | Foreground (having or about to get focus) | Created Started Resumed |
More | Background (lost focus) | Paused |
Most | Background (not visible) | Stopped |
Most | Empty | Destroyed |
可以看出,当Activity处于后台时其进程都是有可能被杀掉的。
Activity和系统配置
理解这个问题,首先要对系统的资源加载机制有一定了解,举个栗子,Activity中加载图片,通常会将图片保存在项目drawable目录中通过resources进行获取,Android为了兼容不同版本所有资源文件都有资源限定符的概念,不清楚的可以看Android资源限定符。这样,当应用启动时就会根据当前设备的情况去加载合适的Resource资源了。
比如,横竖屏可能会拿到两张不同的图片,因此,当Activity在运行中突然发生屏幕旋转情况时,由于系统配置发生了改变,默认情况Activity就会被销毁,当然也可以通过配置阻止销毁,这是后话了。
Activity的异常重建
异常重建的主要方式
上面说到了Activity发生异常销毁的两种可能,那为什么有时即便被销毁了我们依然可以重新看到界面呢,这就是Android的异常重建机制了。
为了保证用户体验,当Activity是在异常情况下终止时系统会调用采用状态保存和回复两套流程来恢复之前的状态。
onSaveInstanceState
保存状态,该方法在onStop
之前,无论是否发生异常均会调用该方法。onRestoreInstanceState
恢复状态,该方法在onStart
之后,仅发生异常时调用。除此之外,我们熟悉的onCreate(savedInstanceState: Bundle?)
也有类似的功能,其中参数savedInstanceState
就是保存的状态。
不同版本调用逻辑略有不同:
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
// Before P onSaveInstanceState was called before onStop, starting with P it's
// called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
final boolean isPreP = r.isPreP();
if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}
try {
// 执行Activity onStop
r.activity.performStop(r.mPreserveWindow, reason);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to stop activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.setState(ON_STOP);
// .....
if (shouldSaveState && !isPreP) {
callActivityOnSaveInstanceState(r);
}
}
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
PendingTransactionActions pendingActions) {
// Pre-Honeycomb apps always save their state before pausing
final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
if (shouldSaveState) {
callActivityOnSaveInstanceState(r);
}
// 执行Activity onPause
performPauseActivityIfNeeded(r, reason);
HONEYCOMB 11 之前
onPause 前调用
P 28之前
onStop 前调用
P 28及之后
onStop 后调用
异常重新的具体流程
通过onSavedInstanceState
和onRestoreInstanceState
,系统自动为我们做了一定的恢复工作,例如文本框的数据,ListView滚动位置等等。那这些具体是如何实现的呢?
首先是Activity被意外终止,调用onSave..
去保存数据,然后Activity会委托Window,Window再委托其上的顶级容器,通常顶级容器就是我们知道的DecorView
。最后由DecorView
遍历自身子元素去一一保存数据。因此特定View
的数据保存和恢复其实是View自身实现的。查看顶级View的源码,发现一样具备onSave..
和onRestore..
两个方法,也就是说如果我们需要对某个View的异常状态进行保存,只需要重写其onSave..
和onRestore..
两个方法就可以了。
重建范例
View的重建主要是通过两个主方法中savedInstanceState: Bundle?
参数完成的。以下是官方文档中举得例子:
companion object {
val STATE_SCORE = "playerScore"
val STATE_LEVEL = "playerLevel"
}
override fun onSaveInstanceState(outState: Bundle?) {
// Save the user's current game state
outState?.run {
putInt(STATE_SCORE, currentScore)
putInt(STATE_LEVEL, currentLevel)
}
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(outState)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
with(savedInstanceState) {
// Restore value of members from saved state
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
} else {
// Probably initialize members with default values for a new instance
}
// ...
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState)
// Restore state members from saved instance
savedInstanceState?.run {
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
}