【Android Activity】Activity的生命周期
知识点
一、 Activity的生命周期
onCreate:表示Activity正在被创建。生命周期的第一个方法,当打开一个activity时首先回调这个方法。在这个方法中一般做一些初始化工作,例如加载界面布局资源(setContentView)、数据初始化(findviewbyid)
onRestart : 表示Activity正在被重新启动。当前activity从不可见变为可见状态时这个方法就会回调。一般是用户行为导致,比如用户摁home键回到桌面,当用户再次回到本activity时,当前activity 走 onRestart->onStart->onResume
onStart :表示activity正在被启动。activity可见,未出现在前台。可以理解为activity已经显示出来了,但是我们看不到,不能与之交互。
onResume :表示activity已经可见,并且出现在前台,可以与用户进行交互。例如activity上有Button,此时我们就可以进行点击了。
onPause:表示activity正在停止,接着很快执行onStop。注意极端情况下,本Activity跳转其他activity后快速的回到当前activity时,当前activity的生命周期:onPause->onResume
但是这个“快速回到”要很快,一般情况下都是onPause->onStop->onRestart->onStart->onResume。这里不能做太耗时操作,可以做一些数据存储,动画停止工作。
onStop:表示activity即将停止,可以做一些回收工作。但是不能太耗时。
onDestroy :activity即将被销毁 ,activity生命周期最后一个回调,这里可以做一些回收工作,资源释放。
二、Activity生命周期图解
三、生命周期的切换过程实践
在面试Activity的基础时,两Activity切换时的生命周期执行顺序经常被考察,这里就以一个简单的🌰来实践下
public class MainActivity extends AppCompatActivity {
public static final String TAG = "tags";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//打开Main2Activity
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, Main2Activity.class));
}
});
Log.i(TAG, "onCreate:");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume: ");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause: ");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop: ");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "onRestart: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
}
}
public class Main2Activity extends AppCompatActivity {
public static final String TAG = "tags";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2);
Log.i(TAG, "Main2Activity onCreate:");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "Main2Activity onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "Main2Activity onResume: ");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "Main2Activity onPause: ");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "Main2Activity onStop: ");
}
@Override
protected void onRestart() {
super.onRestart();
Log.i(TAG, "Main2Activity onRestart: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "Main2Activity onDestroy: ");
}
}
1、 启动MainActivity
MainActivity走 onCreate -> onStart -> onResume
2、由MainActivity跳转到Main2Activity
1、MainActivity 走 onPause
2、Main2Activity走 Main2Activity onCreate -> Main2Activity onStart -> Main2Activity onResume
3、MainActivity 走 onStop
注意
- MainActivity与Main2Activity生命周期有交叉
- 如果此时Main2Activity 已经设置了透明主题则mainActivity是不走onStop的,只走个onPause。从MainActivity跳转到Main2Activity 我们还是可以看见mainActivity,所以验证了onPause的可见不前台。
- 展示对话框不影响生命周期。
3、在MainActivity页面摁Home键
MainActivity 走onPause -> onStop
4、摁Home后再次回到MainActivity时
此时MainActivity走了 onRestart -> onStart-> onResume
5、启动MainActivity 跳转到Main2Activity 再返回到 MainActivity
如上:
红圈1对应启动mainActivity
红圈2对应跳转到Main2Activity
红圈3对应再返回到 mainActivity
6、用户打开MainActivity然后摁back键
MainActivity走了 onPause->onStop->onDestroy
四、 生命周期小结
- 从整个生命周期来说onCreate和onDestroy 是配对的,分别代表着actiity的创建和销毁。并且只可能调用一次。
- 从activity是否可见来说onStart和onStop是配对的,随着用户的操作两个生命周期可能被多次执行。
- 从activity是否在前台来说onResume和onPause是配对的,随着用户的操作两个生命周期可能被多次执行。
五 、 问题思考
1、onStart 和 onResume ,onPause 和onStop从描述上差不多对我们来说有什么实质不同?
从使用过程来讲两对的确差不多,甚至我们可以只保留一对,比如只保留onStart 、onStop。根据上面的分析我们知道,这两对接口代表不同的意义:
onStart 、onStop是从是否可见的角度来回调的(前者可见后者不可见)。
onResume 、onPause,是从是否前台的角度来回调的(前者前台,后者非前台)
2、假设当前activity为A,如果这时用户打开新的activityB,B的onResume 和A的onPause那个先执行
执行结果 A:onPause->B:onCreate->B:onStart->B:onResume->A:onStop
六 、 异常情况下的生命周期
上述的摁Home键、摁返回键、摁按钮跳转页面都是正常情况下用户交互流程。其实Activity的生命周期还受如下因素影响,若如下条件触发时默认情况下Activity会销毁然后重建
- 资源相关的系统配置发生改变:如横竖屏切换、系统语言切换等等。
- 系统内存不足
1、资源相关配置发生改变导致Activity被杀死并重新创建🌰
默认情况下我们打开activity,activity走了 onCreate -> onStart -> onResume
进行屏幕旋转后我们发现activity走了 onPause -> onStop -> onDestroy -> onCreate -> onStart -onResume
可见资源相关配置发生改变,activity确实发生了销毁然后重建。
2、异常情况下数据的存储
activity在异常情况下会发生重建,那么页面上的数据我们如何恢复呢?activity提供两个回调方法用来进行异常情况下数据的存储、恢复。
(1)onSaveInstanceState(Bundle outState)
- activity异常终止时系统会调用此方法来保存activity的状态,例如记录界面上输入框的文字、ListView滑动位置,重建时直接恢复这些状态
- activity异常终止时我们也可以在这个方法中添加逻辑操作,存储我们想要的信息。
- 注意这个方法在activity正常终止是不会回调的。
(2)onRestoreInstanceState(Bundle savedInstanceState)
- 当activity异常终止重建时,系统会回调此方法,并把activity销毁时onSaveInstanceState方法所保存的Bundle 对象作为参数传递给onCreate和onRestoreInstanceState
- 可通过onCreate和onRestoreInstanceState方法判断activity是否被重建。因为正常情况下Activity的onCreate方法的Bundle参数为null。异常情况下Bundle才非空。
下面举个🌰来验证Activity异常终止重建时
- Activity会自动保存页面上UI的数据,并自动恢复。
- 在activity异常终止时存储我们想要存储的信息,在activity重建时获取我们已经存储的信息
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i(TAG, "onSaveInstanceState: ");
//存下自定义数据
outState.putString("tom","异常下存数据 :tom");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.i(TAG, "onRestoreInstanceState: ");
//读取存取的数据
Log.i(TAG, " "+savedInstanceState.get("tom"));
}
输入内容后点击按钮让屏幕旋转:
通过上述截图我们可以看到
- EditText输入的文字在Activity销毁后重建得到恢复。
- 我们自定义的数据在Activity销毁后重建得到恢复。
特别注意EditText控件一定要在xml布局中设置id,否则数据不会被恢复(原因参看EditText源码,内部有处理Activity如何帮view恢复数据的逻辑)
思考:onRestoreInstanceState和onCreate中都可以收到onSaveInstanceState存的Bundle值。这两个方法有啥区别?
- onRestoreInstanceState
只有异常声明周期下这个回调才会被调用(onSaveInstanceState也是)一旦被调用其参数savedInstanceState一定是有值的,我们不用额外判空。 - onCreate
如果是正常启动Activity时,onCreate方法的参数值为空,所以要使用参数值时要额外判空。
3、系统内存不足导致低优先级的activity被杀死
这种情况我们不好模拟,但是其生命周期、数据存储、的相关方法回调和资源配置发生改变回调是完全一致的。
系统内存不足时,系统总会优先杀死低进程的activity
Activity的优先级高低:
-
前台activity:正在和用户交互的activity,优先级最高。
-
可见但非前台activity:比如activity中弹出个对话框,导致activity还是可见,但是无法和用户交互了,优先级次于前台。
-
后台activity:已经被停止的activity,比如执行了onstop。优先级最低。
安卓中的进程优先级从高到低:
前台进程 > 可见进程 > 服务进程 >后台进程 >空进程
七、避免资源配置改变导致Activity重新创建
资源配置改变可以不让activity重新创建吗?答案是有的:
1、在清单文件为activity指定configChanges = xxx,代表xxx模式下activity不会重新创建。
xxx有哪些值呢?可以参考下表
属性值 | 含义 |
---|---|
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 | 当屏幕的尺寸信息发生改变,当旋转设备屏幕时,屏幕尺寸会发生改变,这个选项比较特殊,他和编译选项有关,当编译选项中的minSdkVersion和targetSdkVersion均低于13时此选项不会导致activity重启,否则导致activity重启(api 13新添加) |
smallestScreenSize | 当设备的物理屏幕尺寸信息发生改变,这个属性值和屏幕的方向没关系,仅表示实际的物理屏幕发生改变时触发,比如用户切换到外设的显示设备,和screenSize一样,当编译选项中的minSdkVersion和targetSdkVersion均低于13时此选项不会导致activity重启,否则导致activity重启(api 13新添加) |
layoutDirection | 当布局方向发生改变,这个属性用的比较少正常情况下无法修改布局的layoutDirection(api 17新添加) |
configChanges的属性值如上表,配置多个属性值时使用|链接即可
2、重写activity的onConfigurationChanged方法
在activity发生xxx配置更改时onConfigurationChanged就会回调,这里我们可做一些监听工作。
小结
Activity的生命周期就详细的回顾了下~
更多推荐
所有评论(0)