android Activity生命周期(设备旋转、数据恢复等)与启动模式
博客逐步迁移至 极客兔兔的小站
1.Activity生命周期
接下来将介绍 Android Activity(四大组件之一) 的生命周期, 包含运行、暂停和停止三种状态,onCreate、onStart、onResume、onPause、onStop、onDestroy六种系统调用方法。
1.1 Activity生命周期简介与测试
如图所示,Activity实例可以在生命周期状态发生关键性转换时完成某些工作。
- onCreate()
创建
,该方法是最常被覆盖的方法,第一次创建实例时调用, 一般用来完成实例创建的初始化操作,包括实例化组件,设置监听器,访问外部模型数据等。- onStart()
开始
,当Activity处于可见状态的时候就会调用onStart方法,包括创建完实例显示,或者从其他活动切换到活动时调用。- onResume()
准备
,当Activity获得用户焦点时调用。- onPause
暂停
,当Activity准备调用或者恢复另一个活动时调用,失去焦点时,例如启动一个Dialog调用。该方法可以释放一些消耗CPU的资源,保存一些关键数据,但是执行速度要快,否则影响新的栈顶活动使用。- onStop
停止
,完全不可见时调用。例如成功启动了另外一个活动,该活动离开栈顶不可见。启动Dialog不会执行该方法。- onDestroy
销毁
,这个方法在活动销毁时调用。
// 可以使用以下方法进行测试。
public class MainActivity extends Activity {
private String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG,"Create");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "Start");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "Resume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "Pause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "Stop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "Destroy");
}
}
1.2 设备旋转时的生命周期与数据恢复
使用上面的日志打印测试方法发现, 设备旋转时生命周期如图所示,即设备旋转时,当前活动实例会被系统销毁,然后创建一个新的实例。
因为旋转设备会改变设备配置(device configuration),设备配置包括屏幕的方向、密度、尺寸、键盘类型、底座模式等,为匹配不同的设备,可以使用不同的布局文件,这和Web开发中 针对不同宽度的屏幕选择不同CSS样式异曲同工,如何创建不同布局适配不同配置设备不在本文讨论之列。
设备旋转时,临时数据会丢失。例如你使用了一个变量记录了用户点击了多少次按钮,设备旋转之后,Activity重新创建,这个变量就被初始化了,这里可以使用Bundle
对象来恢复。保存数据可以覆盖onSaveInstanceState()
方法。
例如下面的代码,按3次按钮,旋转屏幕后,将打印出saved currentPage:3 init mCurrentPage:0,点击次数存在了Bundle对象中,并在onCreate()中获取到。
/*
* onSaveInstanceState()通常在onPause、onStop、onDestroy前由系统调用
* onCreate()传入的参数savedInstanceState可获取保存的变量
*/
public class MainActivity extends Activity {
private static final String KEY = "currentPage";
private static final String TAG = "MainActivity";
private Integer mCurrentPage = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentPage += 1;
}
});
if(savedInstanceState != null){
// 打印使用Bundle保存的currentPage信息
Log.d(TAG,"saved currentPage: " + savedInstanceState.getInt(KEY,0));
// 打印新建活动mCurrentPage的值,总为0
Log.d(TAG,"init mCurrentPage: " + mCurrentPage);
// ... update code
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 使用key-value对的方式存储临时变量
outState.putInt(KEY, mCurrentPage);
}
}
1.3 Activity暂存与Activity记录
当系统回收内存时,处于Pause、Stop状态的Activity可能被销毁,此时系统会调用onSaveInstanceState(),用户数据被存储在Bundle对象中,系统将Bundle对象放入Activity记录,可以将这种状态理解为Activity的暂存状态(无实例有记录)。
Activity暂存后,当前活动不复存在,Activity记录可帮助用户返回应用时活动的快速恢复,提供一个好的用户体验。但是当用户按了后退键,系统会彻底销毁当前活动,Activity记录同时被清除,系统重启也会被清除该记录。
2.Activity启动模式
2.1 standard(默认)
standard是活动默认的启动模式,Android是使用返回栈来管理活动的,所谓栈就是先进后出,后进先出。该模式下,每启动一个新的活动,就会在返回栈中入栈,并处于栈顶的位置(即用户当前见到的活动)。系统不会 检查该活动的实例已经在返回栈中存在,每次启动都新建一个。当前返回栈为A->B(B为栈顶),新建活动B, 返回栈变为A->B->B。
2.2 singleTop
standard模式有时并不合理,比如活动实例已经在栈顶了,再次启动时还需再创建一个实例, 这会造成资源的浪费。singleTop模式下,如果当前栈顶已经是该活动实例,则认为可以直接使用,而不再创建新的活动。当前返回栈为A->B(B为栈顶),新建活动B, 返回栈仍为A->B。
<!--AndroidManifest.xml-->
<!--设置方式,launchMode -->
<activity android:name=".MainActivity"
android:launchMode="singleTop"
android:label="AndroidDialog">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
2.3 singleTask
singleTop模式可以避免重复创建栈顶活动的问题,但是如果启动活动不在栈顶,而之前已经创建过,还是会重复创建。例如A->B->C,当前已有三个活动A、B、C,C位于栈顶,再创建B,返回栈变为A->B->C->B(另一个实例)。singleTask模式可以让返回栈中每个活动只存在一个实例,如果发现当前需要启动活动已经在栈中,则直接使用,但是该活动之上的所有活动全部出栈;如没有发现,则创建新的实例。例如A->B->C,如此时创建活动B,则返回栈变为A->B(B存在,使用B,C出栈);若此时创建活动D,则返回栈变为A->B->C->D
2.4 singleInstance
singleInstance模式是四种启动模式中最复杂的,不同于上述三种模式,指定为singleInstance模式的活动会启动一个新的返回栈来管理这个活动。例如当前有活动A、B、C,活动A、C为默认启动模式,B指定为singleInstance模式,首先新建A,A活动页面启动B,B启动C,此时的返回栈并不是A->B->C,而是存在2个返回栈,一个是A->C,另一个是B。此时按下BACK键返回,将从C返回到A,再按一次,A返回到B,按第三次,程序退出。即返回时,先清空栈A->C,再清空栈B。
singleInstance模式有什么应用场景呢?例如当前程序中某个活动允许其他活动调用,如果想多个程序共享该活动的一个实例A,那么每一个程序都有一个返回栈,若使用前3种模式,该活动入栈时必然创建新的实例,无法实现实例共享。singleInstance能很好地解决这个问题。