Android中的onSaveInstanceState和onRestoreInstanceState方法

898 阅读6分钟

Activity的onSaveInstanceState方法

先看一下官方文档:

/** * Called to retrieve per-instance state from an activity before being killed * so that the state can be restored in {@link #onCreate} or * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method * will be passed to both). * *

This method is called before an activity may be killed so that when it * comes back some time in the future it can restore its state. For example, * if activity B is launched in front of activity A, and at some point activity * A is killed to reclaim resources, activity A will have a chance to save the * current state of its user interface via this method so that when the user * returns to activity A, the state of the user interface can be restored * via {@link #onCreate} or {@link #onRestoreInstanceState}. * *

Do not confuse this method with activity lifecycle callbacks such as * {@link #onPause}, which is always called when an activity is being placed * in the background or on its way to destruction, or {@link #onStop} which * is called before destruction. One example of when {@link #onPause} and * {@link #onStop} is called and not this method is when a user navigates back * from activity B to activity A: there is no need to call {@link #onSaveInstanceState} * on B because that particular instance will never be restored, so the * system avoids calling it. An example when {@link #onPause} is called and * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A: * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't * killed during the lifetime of B since the state of the user interface of * A will stay intact. * *

The default implementation takes care of most of the UI per-instance * state for you by calling {@link android.view.View#onSaveInstanceState()} on each * view in the hierarchy that has an id, and by saving the id of the currently * focused view (all of which is restored by the default implementation of * {@link #onRestoreInstanceState}). If you override this method to save additional * information not captured by each individual view, you will likely want to * call through to the default implementation, otherwise be prepared to save * all of the state of each view yourself. * *

If called, this method will occur after {@link #onStop} for applications * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}. * For applications targeting earlier platform versions this method will occur * before {@link #onStop} and there are no guarantees about whether it will * occur before or after {@link #onPause}. * * @param outState Bundle in which to place your saved state. * * @see #onCreate * @see #onRestoreInstanceState * @see #onPause */

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);
}
  • 触发时机

    当Activity被放到后台时回调,比如按HOME键或者打开新的Activity,注意按返回键是不会回调的。

  • 回调时机

    如果targetsdk>=P而且运行在P手机上,就在onStop之后回调;否则,在onStop之前回调, 但不保证是否在onPause之前或之后回调,目前测试发现都是在onPause之后回调。

  • 原理分析

    ActivityThread的callActivityOnStop方法:

      private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
          // Before P onSaveInstanceState was called before onStop, starting with P it's
          // called after. Before Honeycomb state was always saved before onPause.
          final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                  && !r.isPreHoneycomb();
          final boolean isPreP = r.isPreP();
          if (shouldSaveState && isPreP) {
              callActivityOnSaveInstanceState(r);
          }
    
          try {
              r.activity.performStop(false /*preserveWindow*/, reason);
          } catch (SuperNotCalledException e) {
              throw e;
          } catch (Exception e) {
              if (!mInstrumentation.onException(r.activity, e)) {
                  throw new RuntimeException(
                          "Unable to stop activity "
                                  + r.intent.getComponent().toShortString()
                                  + ": " + e.toString(), e);
              }
          }
          r.setState(ON_STOP);
    
          if (shouldSaveState && !isPreP) {
              callActivityOnSaveInstanceState(r);
          }
      }
    

    这里shouldSaveState用于判断是否需要回调,如果按back键返回,mFinished为true,就不会回调了。最终调用callActivityOnSaveInstanceState方法:

      private void callActivityOnSaveInstanceState(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);
          }
      }
    

    注意这里是outState被创建的地方,最终会保存到ActivityClientRecord中。Instrument的callActivityOnSaveInstanceState方法:

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

    Activity的performSaveInstanceState方法:

      final void performSaveInstanceState(Bundle outState) {
          onSaveInstanceState(outState);
          saveManagedDialogs(outState);
          mActivityTransitionState.saveState(outState);
          storeHasCurrentPermissionRequest(outState);
          if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
      }
    

    可以看到,这里就直接回调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);
      }
    

    首先会保存View相关信息,mWindow实际上是PhoneWindow对象,看看它的saveHierarchyState方法:

      public Bundle saveHierarchyState() {
          Bundle outState = new Bundle();
          if (mContentParent == null) {
              return outState;
          }
    
          SparseArray<Parcelable> states = new SparseArray<Parcelable>();
          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其实就是布局的顶层ViewGroup,会调用其saveHierarchyState方法:

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

    ViewGroup和View的dispatchSaveInstanceState方法都有自己的实现,这一点跟Touch事件分发类似,ViewGroup的dispatchSaveInstanceState方法:

      @Override
      protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
          super.dispatchSaveInstanceState(container);
          final int count = mChildrenCount;
          final View[] children = mChildren;
          for (int i = 0; i < count; i++) {
              View c = children[i];
              if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
                  c.dispatchSaveInstanceState(container);
              }
          }
      }
    

    首先会调用View的dispatchSaveInstanceState方法保存当前ViewGroup的信息,然后遍历子View,调用dispatchSaveInstanceState方法,看看View的dispatchSaveInstanceState方法:

      protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
          if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
              mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
              Parcelable state = onSaveInstanceState();
              if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                  throw new IllegalStateException(
                          "Derived class did not call super.onSaveInstanceState()");
              }
              if (state != null) {
                  // Log.i("View", "Freezing #" + Integer.toHexString(mID)
                  // + ": " + state);
                  container.put(mID, state);
              }
          }
      }
    

    可以看到,真正保存View信息的逻辑就在这里,如果View有自己的ID,就回调onSaveInstanceState方法,最终得到一个Parcelable对象,保存到SparseArray中,这里的哈希表,是以View的ID为key的。因此如果View没有设置ID,onSaveInstanceState也就不会被回调了。

    回到最开始Activity的onSaveInstanceState默认实现,继续往下看,接着会保存Fragment的信息,这个先不深入分析,看最后调用Application的dispatchActivitySaveInstanceState方法:

      /* package */ void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
      Object[] callbacks = collectActivityLifecycleCallbacks();
          if (callbacks != null) {
              for (int i=0; i<callbacks.length; i++) {
                  ((ActivityLifecycleCallbacks)callbacks[i]).onActivitySaveInstanceState(activity,
                          outState);
              }
          }
      }
    

    这个ActivityLifecycleCallbacks看起来很眼熟,没错它就是在Application中用来监听所有Activity生命周期的回调,可以看到,所有Activity的onSaveInstanceState,也会统一回调到这里。

Activity的onRestoreInstanceState方法

还是一样先看官方文档:

/** * This method is called after {@link #onStart} when the activity is * being re-initialized from a previously saved state, given here in * savedInstanceState. Most implementations will simply use {@link #onCreate} * to restore their state, but it is sometimes convenient to do it here * after all of the initialization has been done or to allow subclasses to * decide whether to use your default implementation. The default * implementation of this method performs a restore of any view state that * had previously been frozen by {@link #onSaveInstanceState}. * *

This method is called between {@link #onStart} and * {@link #onPostCreate}. * * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}. * * @see #onCreate * @see #onPostCreate * @see #onResume * @see #onSaveInstanceState */ protected void onRestoreInstanceState(Bundle savedInstanceState) { if (mWindow != null) { Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG); if (windowState != null) { mWindow.restoreHierarchyState(windowState); } } }

  • 触发时机

    当Activity被系统意外回收后,重新进入页面时会被回调。

  • 回调时机

    在onStart之后被回调,也就是 onStart --> onRestoreInstanceState --> onResume

  • 与onCreate区别

    主要的区别是参数问题,onCreate的savedInstanceState可能为空,使用时需要判断,而onRestoreInstanceState的savedInstanceState保证不为空。另外一个区别是回调时机不同,取决于要在哪个阶段进行恢复,如果要先在onCreate中做一些初始化后再恢复,就采用onRestoreInstanceState方法。

  • 原理分析

    ActivityThread的handleStartActivity方法:

      @Override
      public void handleStartActivity(ActivityClientRecord r,
              PendingTransactionActions pendingActions) {
          final Activity activity = r.activity;
          if (r.activity == null) {
              // TODO(lifecycler): What do we do in this case?
              return;
          }
          if (!r.stopped) {
              throw new IllegalStateException("Can't start activity that is not stopped.");
          }
          if (r.activity.mFinished) {
              // TODO(lifecycler): How can this happen?
              return;
          }
    
          // Start
          activity.performStart("handleStartActivity");
          r.setState(ON_START);
    
          if (pendingActions == null) {
              // No more work to do.
              return;
          }
    
          // Restore instance state
          if (pendingActions.shouldRestoreInstanceState()) {
              if (r.isPersistable()) {
                  if (r.state != null || r.persistentState != null) {
                      mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                              r.persistentState);
                  }
              } else if (r.state != null) {
                  mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
              }
          }
          ......
      }
    

    可以看到,这里会先调用performStart方法,最终就是回调Activity的onStart方法,接着Instrumentation的callActivityOnRestoreInstanceState方法,在调用前会对r.state进行判空,也就是前面说的传入的参数保证不为空的原因。另外注意这个r.state其实就是上面onSaveInstanceState保存的那个Bundle对象。看看Instrumentation的callActivityOnRestoreInstanceState方法:

      public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
          activity.performRestoreInstanceState(savedInstanceState);
      }
    

    看Activity的performRestoreInstanceState方法:

      final void performRestoreInstanceState(Bundle savedInstanceState) {
          onRestoreInstanceState(savedInstanceState);
          restoreManagedDialogs(savedInstanceState);
      }
    

    然后就直接回调onRestoreInstanceState方法了:

      protected void onRestoreInstanceState(Bundle savedInstanceState) {
          if (mWindow != null) {
              Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
              if (windowState != null) {
                  mWindow.restoreHierarchyState(windowState);
              }
          }
      }
    

    这里会取出之前保存的View信息windowState,然后调用PhoneWindow的restoreHierarchyState对View进行恢复,流程跟onSaveInstanceState保存View信息差不多,这里不再分析。