onSaveInstanceState 和 onRestoreInstanceState

Posted by Allen Vork on March 24, 2018

作用

当 activity 变得容易销毁的时候,activity、activity 内的 View 和 Fragment 会调用 onSaveInstanceState 方法来存储数据。如果 Activity 销毁了,则会使用 onRestoreInstanceState 来恢复数据(onSaveInstanceState 中保存的数据在恢复时也会传给 onCreate)。

调用场景

onSaveInstanceState

  • 按下 Home 键,Activity 进入后台时
  • 按下电源键,屏幕关闭,Activity 进入后台
  • 启动其他 Activity
  • 横竖屏切换

如果是用户点击 back,或者主动调用 finish 方法来关闭 Activity,而不是 Activity 进入后台后被杀掉的话,是不会调用的。

onRestoreInstanceState

在 Activity 被系统销毁后,恢复 Activity 时会调用。

可以看出 onSaveInstanceState 方法 和 onRestoreInstanceState 方法并不是成对出现的。

调用时机

onSaveInstanceState 出现在 onStop 或 onPause 之前,onRestoreInstanceState 出现在 onstart 与 onResume 之间。
下面是旋转屏幕时,Activity 的生命周期的变化图: 注意,onSaveInstanceState 并不一定在 onPause 之后调用。

onCreate(Bundle savedInstanceState) 中既然能恢复数据,为什么还需要 onRestoreInstanceState?

如果是在 onCreate 中进行恢复数据的话,会导致每次创建(而非恢复)Activity 的时候也要有处理这个 bundle (判断是否为空)的逻辑。而且用 onRestoreInstanceState 方法,我们可以将恢复的逻辑和创建的逻辑解耦。

什么时候用到 onSaveInstanceState?

由于 View 中都有默认的 onSaveInstanceState 方法,只要这个 View 有 id,当 Activity 被系统杀死时,它会自动保存 UI 的一些状态。譬如往 EditText 中输入数据后,切换横屏,这时候数据是自动被保存的,不需要我们去重写 onSaveInstanceState 进行处理。所以一般情况下,我们是不需要重写的。


源码解析

默认的 onSaveInstanceState 保存了些什么

    protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        getApplication().dispatchActivitySaveInstanceState(this, outState);
    }

可以看到它调用了 mWindow.saveHierarchyState 来遍历 Activity 的 view 树,然后调用 mFragments.saveAllState 来保存 Fragment 的数据。

  • 首先我们来看看 window.saveHierarchyState:
    window 是一个抽象类,实现类是 PhoneWindow,我们来看看它的 saveHierarchyState:
      public Bundle saveHierarchyState() {
          Bundle outState = new Bundle();
          if (mContentParent == null) {
              return outState;
          }
    
          SparseArray<Parcelable> states = new SparseArray<Parcelable>();
    
          //保存 Activity 中的 View 的状态
          mContentParent.saveHierarchyState(states);
          outState.putSparseParcelableArray(VIEWS_TAG, states);
    
          // Save the focused view ID.
          final View focusedView = mContentParent.findFocus();
          if (focusedView != null && focusedView.getId() != View.NO_ID) {
              outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
          }
    
          // save the panels
          SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
          savePanelState(panelStates);
          if (panelStates.size() > 0) {
              outState.putSparseParcelableArray(PANELS_TAG, panelStates);
          }
    
          if (mDecorContentParent != null) {
              SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
              mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
              outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
          }
    
          return outState;
      }
    

    它调用了 mContentParent.saveHierarchyState(states),mContentParent 是 DecorView 或者 DecorView 的 child。由于它是一个 ViewGroup,没有 saveHierarchyState 方法,这里它掉的是 View 的方法:

      public void saveHierarchyState(SparseArray<Parcelable> container) {
          dispatchSaveInstanceState(container);
      }
    

    再来看看 ViewGroup 和 View 的 dispatchSaveInstanceState 方法:

      protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
          //ViewGroup 会调用 View 的 dispatchSaveInstanceState 来保存自己的状态
          super.dispatchSaveInstanceState(container);
          final int count = mChildrenCount;
          final View[] children = mChildren;
    
          //遍历子 View 调用子 View 的方法来保存状态
          for (int i = 0; i < count; i++) {
              View c = children[i];
              if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                  c.dispatchSaveInstanceState(container);
              }
          }
      }
    
      protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
          if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
              mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
    
              //获得子 View 的状态
              Parcelable state = onSaveInstanceState();
              if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                  throw new IllegalStateException(
                          "Derived class did not call super.onSaveInstanceState()");
              }
              if (state != null) {
                  //将子 View 的状态保存到 Activity 的 SparseArray 中。注意,它的 key 为 View 的 id。
                  //如果 View 没有设置 id 的话,状态是不会被保存的
                  container.put(mID, state);
              }
          }
      }
    
  • 再来看看如何保存 Fragment 的状态的:
    它最终是调用到 FragmentMangerImpl 的 SaveAllState 方法:
      Parcelable saveAllState() {
          ...
          // First collect all active fragments.
          int N = mActive.size();
          FragmentState[] active = new FragmentState[N];
          boolean haveFragments = false;
    
          //遍历 Fragment 并给每一个 Fragment 创建一个 FragmentState 保存状态
          for (int i=0; i<N; i++) {
              Fragment f = mActive.valueAt(i);
              if (f != null) {
                  ...
                  if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                      //保存 Fragment 基本状态
                      fs.mSavedFragmentState = saveFragmentBasicState(f);
    
                      if (f.mTarget != null) {
                          if (fs.mSavedFragmentState == null) {
                              fs.mSavedFragmentState = new Bundle();
                          }
                          putFragment(fs.mSavedFragmentState,
                                  FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                          if (f.mTargetRequestCode != 0) {
                              fs.mSavedFragmentState.putInt(
                                      FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                      f.mTargetRequestCode);
                          }
                      }
    
                  } else {
                      fs.mSavedFragmentState = f.mSavedFragmentState;
                  }
              }
          }
    
          int[] added = null;
          BackStackState[] backStack = null;
    
          // Build list of currently added fragments.
          N = mAdded.size();
          if (N > 0) {
              added = new int[N];
              for (int i = 0; i < N; i++) {
                  added[i] = mAdded.get(i).mIndex;
              }
          }
    
          // Now save back stack.
          if (mBackStack != null) {
              N = mBackStack.size();
              if (N > 0) {
                  backStack = new BackStackState[N];
                  for (int i=0; i<N; i++) {
                      backStack[i] = new BackStackState(mBackStack.get(i));
                  }
              }
          }
    
          FragmentManagerState fms = new FragmentManagerState();
          fms.mActive = active;
          fms.mAdded = added;
          fms.mBackStack = backStack;
          if (mPrimaryNav != null) {
              fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
          }
          fms.mNextFragmentIndex = mNextFragmentIndex;
          saveNonConfig();
          return fms;
      }
    

    我们看看 saveFragmentBasicState 中保存了哪些基本状态:

      Bundle saveFragmentBasicState(Fragment f) {
          Bundle result = null;
    
          if (mStateBundle == null) {
              mStateBundle = new Bundle();
          }
          //调用 Fragment 的 onSaveInstanceState 方法,并调用子 Fragment 的 saveAllState 方法
          f.performSaveInstanceState(mStateBundle);
          dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
          if (!mStateBundle.isEmpty()) {
              result = mStateBundle;
              mStateBundle = null;
          }
    
          if (f.mView != null) {
              //保存 Fragment 的 View 数据,最终调用到 View 的 saveHierarchyState,和前面保存 Activity 的 View 一样。
              saveFragmentViewState(f);
          }
          if (f.mSavedViewState != null) {
              if (result == null) {
                  result = new Bundle();
              }
              //将上面保存的 View 的数据保存到 Bundle 中
              result.putSparseParcelableArray(
                      FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
          }
          if (!f.mUserVisibleHint) {
              if (result == null) {
                  result = new Bundle();
              }
              // Only add this if it's not the default value
              result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
          }
    
          return result;
      }
    

    我们可以看出,saveAllState() 方法会像 Activity 那样,保存 Fragment 中 View 数的状态。

onSaveInstanceState 是如何被调用的?

  • 我们首先来看看 Activity 在 pause 的时候会不会调用 onSaveInstanceState 方法:
      final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
              boolean saveState, String reason) {
          if (r.paused) {
              if (r.activity.mFinished) {
                  // If we are finishing, we won't call onResume() in certain cases.
                  // So here we likewise don't want to call onPause() if the activity
                  // isn't resumed.
                  return null;
              }
              RuntimeException e = new RuntimeException(
                      "Performing pause of activity that is not resumed: "
                      + r.intent.getComponent().toShortString());
              Slog.e(TAG, e.getMessage(), e);
          }
          if (finished) {
              r.activity.mFinished = true;
          }
    
          // Next have the activity save its current state and managed dialogs...
          if (!r.activity.mFinished && saveState) {
              callCallActivityOnSaveInstanceState(r);
          }
    
          performPauseActivityIfNeeded(r, reason);
    
          // Notify any outstanding on paused listeners
          ArrayList<OnActivityPausedListener> listeners;
          synchronized (mOnPauseListeners) {
              listeners = mOnPauseListeners.remove(r.activity);
          }
          int size = (listeners != null ? listeners.size() : 0);
          for (int i = 0; i < size; i++) {
              listeners.get(i).onPaused(r.activity);
          }
    
          return !r.activity.mFinished && saveState ? r.state : null;
      }
    

    在 PerformPauseActivity 的时候,会调用 callCallActivityOnSaveInstanceState()。但是有一个条件 r.activity.mFinished && saveState。mFinished 值只有当调用了 finish() 方法才会为 true。而这里刚调用 onPause 方法,所以 mFinished 为 false。那么只有 saveState 返回 true 才会执行该方法。 saveState 是由下面这个函数来决定的:

          public boolean isPreHoneycomb() {
              if (activity != null) {
                  return activity.getApplicationInfo().targetSdkVersion
                          < android.os.Build.VERSION_CODES.HONEYCOMB;
              }
              return false;
          }
    

    只要 targetSdk < 11 就返回 true。那么意思就是只要没有调用 finish() 方法,则不会调用 callCallActivityOnSaveInstanceState 方法。

      private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
          r.state = new Bundle();
          r.state.setAllowFds(false);
          if (r.isPersistable()) {
              r.persistentState = new PersistableBundle();
              mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                      r.persistentState);
          } else {
              mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
          }
      }
    

    Instrumentation 里的该函数:

    public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,  
              PersistableBundle outPersistentState) {  
          activity.performSaveInstanceState(outState, outPersistentState);  
      }  
    

    再看看 activity 的 performSaveInstanceState:

    final void performSaveInstanceState(Bundle outState) {  
          onSaveInstanceState(outState);  
          saveManagedDialogs(outState);  
          mActivityTransitionState.saveState(outState);  
      }  
    

    总结:只有在 andriod 3.0 之前并且没有调用 finish() 才会在 onPause 之前执行 onSavedInstanceState 方法。

  • 下面来看看 Activity 在 stop 的时候会不会调用 onSaveInstanceState 方法:
    在 Activity 执行 onStop 时会调用到 ActivityThread 中的 handleStopActivity() 方法,最终会调用到:
      private void performStopActivityInner(ActivityClientRecord r,
              StopInfo info, boolean keepShown, boolean saveState, String reason) {
          if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
          if (r != null) {
              if (!keepShown && r.stopped) {
                  if (r.activity.mFinished) {
                      // If we are finishing, we won't call onResume() in certain
                      // cases.  So here we likewise don't want to call onStop()
                      // if the activity isn't resumed.
                      return;
                  }
                  RuntimeException e = new RuntimeException(
                          "Performing stop of activity that is already stopped: "
                          + r.intent.getComponent().toShortString());
              }
    
              // One must first be paused before stopped...
              performPauseActivityIfNeeded(r, reason);
    
              // Next have the activity save its current state and managed dialogs...
              if (!r.activity.mFinished && saveState) {
                  if (r.state == null) {
                      callCallActivityOnSaveInstanceState(r);
                  }
              }
          }
      }
    

    可以看出和 pause 一样,这个函数的调用取决于 mFinisheded 和 saveState 决定,不同的是:

      private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
          ...
          StopInfo info = new StopInfo();
          performStopActivityInner(r, info, show, true, "handleStopActivity");
          ...
      }
    

    在 handleStopActivity 里 saveState 直接为 true,那么就会执行。

  • 下面再来看看为什么点击返回是不会执行 onSaveInstanceState 方法:
      public void finishAfterTransition() {
          if (!mActivityTransitionState.startExitBackTransition(this)) {
              finish();
          }
      }
    

    点击 back 键会触发 onBackPressed 最终会调用到 finish() 方法,将 mFinished 设置为 true,所以这种间接调用 finish() 方法也是不会调用 onSaveInstanceState 方法的。