Activity的状态保存和恢复

参考 Android 8.0 源码
  在我们的应用在后台运行时,可能会因为安卓系统的内存不足而被系统杀死,虽然我们的应用还能够在系统的运行应用列表中显示,但是我们再次返回应用的时候,其实是相当于重新打开了应用。我们如果再次进入到我们先前在的页面,我们在页面所操作的数据(如在页面中输入了文本)就被清空了。如果我们想要在再次进入该页面,能够获取到先前操作的数据,就需要使用Activity自带的保存状态的方法。主要涉及到的方法有三个,如下:

protected void onSaveInstanceState(@NonNull Bundle outState)
protected void onCreate(Bundle savedInstanceState)
protected void onRestoreInstanceState(Bundle savedInstanceState)

  如果由于系统的原因破坏了Activity,那么尽管实际上Activity实例已经消失了,但是系统还是会记住Activity存在过,这样当用户从运行应用列表(点击手机的菜单键,选择我们的应用)再次进入到应用,系统会创建
一个新的Activity实例,新的Activity会使用一组保存的数据来恢复Activity在被销毁时的状态。系统用于恢复以前状态中的已保存数据称为“实例状态”,是存储在Bundle对象中的键值对的集合。

基本介绍

  默认情况下,系统使用Bundle实例状态来保存有关View中Activity布局每个对象的信息(比如输入到EditText对象中的文本值)。因此,如果我们的Activity实例被销毁并重新创建以后,布局的状态会自动恢复到之前的状态。但是,我们的Activity可能包含更多要恢复的状态信息,这些状态信息就不会被保存了。
  在Activity的生命周期中,还有两个方法就是专门用来保存实例状态和恢复实例状态的,就是onSaveInstanceState()和onRestoreInstanceState()。我们在onSaveInstanceState()方法中保存数据到Bundle中,在onRestoreInstanceState()方法中获取到Bundle中的数据,就可以恢复数据啦。
  准确地来说,调用顺序是这样的,我们的Activity实例被停止时(停止的原因有好几种,会在下面详细讲述),就会调用onSaveInstanceState()方法。当我们再次进入到上次应用最后运行的Activity页面时,其实就是重新创建了新的Activity实例,但是我们可以在onCreate()和onRestoreInstanceState()方法中的参数savedInstanceState(是一个Bundle)中获取到我们在onSaveInstanceState()方法中保存的数据,就可以用这些数据来恢复Activity。对用户而言,Activity页面的状态和先前一样,并不会感知到Activity被杀掉过。
  调用顺序如下:

onSaveInstanceState() -> onCreate() -> onRestoreInstanceState()

  onSaveInstanceState()方法可能在onPause()方法之后调用,也可能在onPause()方法之前调用(我测试的都是在onPause()方法之后调用),但是一定是在onStop()方法之前调用的。我们可以在onCreate()方法中恢复数据,也可以在onRestoreInstanceState()方法中恢复数据。onRestoreInstanceState()方法是在onStart()和onResume()方法之间被调用的。

保存和恢复实例状态

保存实例状态

  我们已经知道是在onSaveInstanceState()方法中来保存实例状态的了。那么我们就用代码举例,比如我们保存一个String类型的数据。为了方便,这里我把String类型的数据给写死了。代码如下:

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    Log.e(TAG, "onSaveInstanceState...");
    outState.putString("bundle_content", "this is content saved");
}

恢复实例状态

  我们也知道了可以在onCreate()方法中恢复数据,也可以在onRestoreInstanceState()方法中恢复数据。实例工作中,我们只选择在其中一个方法中来恢复实例状态就可以了。这里我把这两个方法都写了出来,代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_save_instance);
    Log.e(TAG, "onCreate...");
    if (savedInstanceState != null) {
        //savedInstanceState不为null,说明先前创建过该Activity,但是被系统杀掉了;
        Log.e(TAG, "onCreate savedInstanceState != null...");
        //这里就是获取先前保存的数据;
        String content = savedInstanceState.getString("bundle_content");
        Log.e(TAG, "onCreate content...:" + content);
    } else {
        //savedInstanceState是null,也就意味着这是第一次创建这个Activity;
        Log.e(TAG, "onCreate savedInstanceState is null...");
    }
    //---;
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    Log.e(TAG, "onRestoreInstanceState...");
    String content = savedInstanceState.getString("bundle_content");
    Log.e(TAG, "onRestoreInstanceState content...:" + content);
}

  在onCreate()方法中,我们需要判断savedInstanceState参数是否为null,只有不是null的时候,才是进行恢复实例状态的情景。而在onRestoreInstanceState()方法中,我们没有进行判断savedInstanceState参数是否为null,是因为只要调用了onRestoreInstanceState()方法,就说明一定是在进行实例状态的恢复,savedInstanceState参数一定不是null。

onSaveInstanceState()和onRestoreInstanceState()被调用的情景

onSaveInstanceState()被调用的情景

1 . 当用户按下HOME键以后;
2 . 当用户按下任务键,选择运行其他应用以后;
3 . 当用户按下电源按键,屏幕息屏以后;
4 . 当屏幕方向切换以后(需要Activity没有设置configChanges属性),比如从竖屏切换到了横屏;
5 . 跳转到其他页面的以后;

  以上几种情况,系统不知道我们操作了以后,我们的Activity是否会被销毁,因此系统就会调用onSaveInstanceState()方法,让我们有机会去保存某些数据。

onRestoreInstanceState()被调用的情景

  该方法被调用的前提是,Activity确实被系统销毁了。而如果仅仅是停留在有这种可能的情况下,该方法不会被调用的。上面讲的onSaveInstanceState()被调用的五种情景中,其中的第四点情景,也就是屏幕方向切换之后系统会销毁Activity然后又重新创建一个新的Activity,这种情况下onSaveInstanceState()会执行,而且也一定会执行onRestoreInstanceState()方法。
  可以这样认为,当Activity的onDestroy()方法不是在我们的代码中被调用的,而是系统主动调用的,那么onRestoreInstanceState()才会被调用。

onSaveInstanceState()方法的默认实现

  Android应用框架中的大多数UI控件都实现了onSaveInstanceState()方法,这个方法默认实现了自动保存Activity中的View的某些状态数据,因此当Activity被销毁和重建的时候,这些Viewhi自动保存和恢复状态数据。比如EditText会自动保存和恢复我们输入的文本数据,而CheckBox会自动保存和恢复选中状态。但是,需要我们为这些控件指定一个唯一的ID,也就是设置android:id这个属性,保存和恢复状态数据这两件事就会由View自动来完成了。但是如果我们没有为控件指定ID,View就不会进行自动的状态数据保存和恢复。所以,我们重写onSaveInstanceState()方法,第一行代码super.onSaveInstanceState(outState),其实就是调用该方法自身的默认实现,做的自动保存和恢复View的某些状态数据的。
  但是,我们实际开发中,需要保存一般还有其他一些额外的数据,就需要覆写onSaveInstanceState()方法。需要注意的是,onSaveInstanceState()方法只适合保存瞬间状态数据,比如成员变量的值,而不应该用来保存持久化的数据。因为当我们主动离开Activity页面调用onDestroy()方法之后,并不会调用onSaveInstanceState()方法。

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
--------------------last line for now------------------