异常情况下的生命周期分析
一:资源相关的系统配置发生改变导致Activity被杀死并重新创建(以旋转屏幕为例)
样例代码,
首先再onSaveInstanceState
中存储一个字符串,然后当 Activity
被销毁并重新创建的时后,再去获取之前存储的字符串。接收的位置可以在 onRestoreInstanceState
或者onCreate
,二者的区别是: onRestoreInstanceState
一旦被调用其参数Bundle savedInstanceState
一定是有值的,不需要额外判空;但onCreate
不行,因为onCreate
如果是正常启动,其参数Bundle savedInstanceState
为 null
,所以必须进行额外判断。官方推荐采用 onRestoreInstanceState
进行恢复数据。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null){
String test = savedInstanceState.getString("test");
Log.d(TAG, "[onCreate] restore extra_test: " + test);
}
}
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
Log.d(TAG,"onSaveInstanceState");
outState.putString("test","test");
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String test = savedInstanceState.getString("test");
Log.d(TAG,"[onRestoreInstanceState] restore test: " + test);
}
当旋转屏幕后,运行的日志如下:
日志分析
当系统配置发生改变后,Activity 会被销毁,其onPause、onStop、onDestroy 均会被调用,同时由于Activity 是在异常情况下终止的,系统会调用 onSaveInstanceState 来保存当前Activity的状态。这个方法的调用时机是在 onStop 之前,它和 onPause 没有既定的时序关系,它既可能在onPause之前调用,也可能在onPause之后调用。(当Android api为28时,onStop会在其之前,即 onSaveInstanceState 出现在 onStop之后,onDestroy之前,与日志情况相符)需要强调的一点是,这个方法只会出现在Activity 被异常终止的情况下,正常情况下系统不会回调这个方法。当 Activity 被重新创建后,系统会调用 onRestoreInstanceState, 并且把Activity 销毁时onSavelnstanceState 方法所保存的Bundle 对象作为参数同时传递给 onRestoreInstanceState和 onCreate 方法。因此,我们可以通过 onRestoreInstanceState 和 onCreate 方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestorelnstanceState 的调用时机在onStart之后。
onSaveInstanceState调用时机
- 当用户按下HOME键时。 这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
- 长按HOME键,选择运行其他的程序时
- 按下电源按键(关闭屏幕显示)时
- 从activity A中启动一个新的activity时
- 屏幕方向切换时,例如从竖屏切换到横屏时。
onSaveInstanceState
的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity
,则onSaveInstanceState
会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)
避免当系统配置发生改变后,Activity被重新创建
给configChanges
属性添加值
例如解决旋转屏幕的重新创建:android:configChanges="orientation|screenSize"
主要使用的几个值
- keyboardHidden : 键盘的可访问性发生了改变,例如调出了键盘
- orientation : 屏幕方向发生了改变,例如旋转了屏幕
- screenSize : 当屏幕的尺寸信息发省了改变,当旋转屏幕时,屏幕尺寸会发生改变,但这个选项比较特殊和编译选项有关,当编译选项中的 minSdkVersion 和 targetSdkVersion 均低于13时,此选项不会导致 Activity 重启,否则会导致 Activity 重启(API 13 新添加)
- locale : 设备的本地位置发生了改变,一般指切换了系统语言
二:资源内存不足情况导致低优先级的Activity被杀死
这种情况我们不好模拟,但是其数据存储和恢复过程和情况一完全一致。这里我们描述一下Activity的优先级情况。Activity 按照优先级从高到低,可以分为如下三种:
-
前台Activity-- 正在和用户交互的Activity, 优先级最高。
-
可见但非前台Activity——比如 Activity中弹出了一个对话框,导致Activity可见,但是位于后台无法和用户直接交互。
-
后台Activity--已经 被暂停的Activity,比如执行了onStop, 优先级最低。
当系统内存不足时,系统就会按照上述优先级去杀死目标 Activity 所在的进程,并在后续通过 onSaveInstanceState 和 onRestoreInstanceState 来存储和恢复数数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死,因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。