一、作用
Activity中的onSaveInstanceState()、onRestoreInstanceState()两个方法是用于保存Activity中的状态的。当Activity不是正常方式退出,且Activity在随后的时间内被系统杀死之前会调用者两个方法让开发人员可以有机会存储Activity相关的信息,并且在下次返回Activity时恢复这些数据。
二、触发时机
onSaveInstanceState并不是在每次Activity推出之前都会调用,只是在某些特定的情况之下它才会被调用,这些特定场景总结为一句话就是:当摸个Activity变得容易被系统销毁时,该Activity的onSaveInstanceState函数就会被执行,除非该Activity是被用户主动销毁的,如直接按back键时。
onSaveInstanceState被调用的几种可能情况:
1、当用户按下Home键时。
2、长按Home键,选择运行其他的程序时。
3、按下电源键(关闭屏幕显示时)。
4、从Activity A中启动一个新的Activity时。
5、屏幕方向切换时,如从竖屏切换到横屏时。
6、电话打入等情况。。。
三、源码分析
首先看下onSavaInstanceState的方法源码:
/**
* 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).
*
* <p>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}.
*
* <p>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.
*
* <p>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.
*
* <p>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);
//存储Fragment的状态
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
// 当保存的状态数据不为空时,判断是否自动保存状态恢复(默认是false)
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
//如果用户还设置了Activity的ActivityLifecycleCallbacks
//那么调用这些ActivityLifecycleCallbacks的onSaveInstanceState进行存储状态
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
上述onSaveInstanceState函数中,主要分为3步:
1、存储当前窗口的视图树的状态
2、存储Fragment的状态
3、调用这些ActivityLifecycleCallbacks的onSaveInstanceState进行存储状态(关于“ActivityLifecycleCallbacks”不了解的百度一下就明白了)
首先第一步,在这一步将Window对象中的视图树中各个View状态存储到Bundle中,这样一来,当用户重新进入到该Activity时,用户UI的结构、状态才会被重新恢复,以此来保证用户界面的一致性。Window类的具体实现类是PhoneWindow,具体saveHierarchyState函数的实现如下:
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
//这个mContentParent是调用Activity的setContentView函数设置的内容视图,它是内容视图的根节点,在这里存储整颗视图树的结构
if (mContentParent == null) {
return outState;
}
//通过SparseArray类来存储,这相当于一个key为整型的map
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//调用mContentParent的saveHierarchyState方法,将整个视图树结构保存到states中
mContentParent.saveHierarchyState(states);
//将视图树结构放到outState中
outState.putSparseParcelableArray(VIEWS_TAG, states);
// Save the focused view ID.
//保存当前界面中获取了焦点的View
final View focusedView = mContentParent.findFocus();
//持有焦点的View必须要设置id,否则重新进入该界面时不会恢复它的焦点状态
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);
}
//存储ActionBar的状态
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
在saveHierarchyState函数中,主要是存储了与当前UI、ActionBar相关的View状态,这里用mContentParent来分析。这个mContentParent就是我们通过Activity的setContentView函数设置的内容视图,它是整个内容视图的根节点,存储它层级结构中的View状态也就存储了用户界面的状态,mContentParent是一个ViewGroup对象,但是saveHierarchyState并不是在ViewGroup中,而是在ViewGroup的父类View中,具体源码如下:
/**
* Store this view hierarchy's frozen state into the given container.
*
* @param container The SparseArray in which to save the view's state.
*
* @see #restoreHierarchyState(android.util.SparseArray)
* @see #dispatchSaveInstanceState(android.util.SparseArray)
* @see #onSaveInstanceState()
*/
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
/**
* Called by {@link #saveHierarchyState(android.util.SparseArray)} to store the state for
* this view and its children. May be overridden to modify how freezing happens to a
* view's children; for example, some views may want to not store state for their children.
*
* @param container The SparseArray in which to save the view's state.
*
* @see #dispatchRestoreInstanceState(android.util.SparseArray)
* @see #saveHierarchyState(android.util.SparseArray)
* @see #onSaveInstanceState()
*/
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//注意:如果View没有设置id,那么这个View的状态将不会被存储
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
//调用onSaveInstanceState函数获取自身的状态
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中,key为id,value为自身状态
container.put(mID, state);
}
}
}
/**
* Hook allowing a view to generate a representation of its internal state
* that can later be used to create a new instance with that same state.
* This state should only contain information that is not persistent or can
* not be reconstructed later. For example, you will never store your
* current position on screen because that will be computed again when a
* new instance of the view is placed in its view hierarchy.
* <p>
* Some examples of things you may store here: the current cursor position
* in a text view (but usually not the text itself since that is stored in a
* content provider or other persistent storage), the currently selected
* item in a list view.
*
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save.
* @see #onRestoreInstanceState(Parcelable)
* @see #saveHierarchyState(SparseArray)
* @see #dispatchSaveInstanceState(SparseArray)
* @see #setSaveEnabled(boolean)
*/
@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null || isAutofilled()
|| mAutofillViewId > LAST_APP_AUTOFILL_ID) {
View.BaseSavedState state = new View.BaseSavedState(AbsSavedState.EMPTY_STATE);
if (mStartActivityRequestWho != null) {
state.mSavedData |= View.BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED;
}
if (isAutofilled()) {
state.mSavedData |= View.BaseSavedState.IS_AUTOFILLED;
}
if (mAutofillViewId > LAST_APP_AUTOFILL_ID) {
state.mSavedData |= View.BaseSavedState.AUTOFILL_ID;
}
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
state.mIsAutofilled = isAutofilled();
state.mAutofillViewId = mAutofillViewId;
return state;
}
//View的默认存储状态为空
return View.BaseSavedState.EMPTY_STATE;
}
通过上述代码得知,默认View的存储状态是空,因为存储key值是id,所以就要求我们需要view设置id属性,且同一个activity中的所有view,id不能一致,否则会被覆盖。
另外我们看一下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);
}
}
}
ViewGroup中的dispatchSaveInstanceState函数会调用super.dispatchSaveInstanceState()函数存储自身的状态,然后遍历所有子视图,并且调用子视图的dispatchSaveInstanceState函数来存储它们的状态。如果子视图是ViewGroup类型,那么则再次执行这个过程。
不过在View类中我们看到返回的状态是一个空状态,这就意味着当我们需要存储View的状态时,需要腹泻onSaveInstanceState方法,将要存储的数据放到Parcelable对象中,并且将它返回。这在我们实现自定义View的时候需要特别注意。
在系统提供的各种子View中都已经重新了此方法,下面我们看下TextView的onSaveInstanceState实现:
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
final boolean freezesText = getFreezesText();
boolean hasSelection = false;
int start = -1;
int end = -1;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
hasSelection = true;
}
}
//存储TextView的start、end及文本内容
if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
if (freezesText) {
if (mText instanceof Spanned) {
final Spannable sp = new SpannableStringBuilder(mText);
if (mEditor != null) {
removeMisspelledSpans(sp);
sp.removeSpan(mEditor.mSuggestionRangeSpan);
}
ss.text = sp;
} else {
ss.text = mText.toString();
}
}
if (hasSelection) {
// XXX Should also save the current scroll position!
ss.selStart = start;
ss.selEnd = end;
}
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
ss.error = getError();
if (mEditor != null) {
ss.editorState = mEditor.saveInstanceState();
}
//返回状态对象
return ss;
}
return superState;
}
如此经过一层一层地遍历后,整个视图树的状态就被存储起来了。
存储完Window的视图树状态信息之后,便会执行存储Fragment中的状态信息、回退栈等。这个存储Fragment状态信息也是调用它的onSaveInstanceState方法,存储Fragment的VIew视图树状态,最后就是调用用户设置的ActivityLifecycleCallbacks的onSaveInstanceState方法,让用户能够再做一些额外的处理。至此,整个存储过程就完成了。
下面我们再来看一下,那些存储了状态信息的Bundle数据存储在哪里呢?
首先onSaveInstanceState方法调用是在Activity被销毁之前,更确切的说是在调用onStop方法之前。Activity的onStop方法在ActivityThread的performStopActivity函数中,相关代码如下:
/** Called from {@link LocalActivityManager}. */
final void performStopActivity(IBinder token, boolean saveState, String reason) {
//获取ActivityClientRecord
ActivityClientRecord r = mActivities.get(token);
//执行performStopActivityInner方法,其中saveState标识是否要存储状态
performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState,
false /* finalStateRequest */, reason);
}
/**
* Core implementation of stopping an activity. Note this is a little
* tricky because the server's meaning of stop is slightly different
* than our client -- for the server, stop means to save state and give
* it the result when it is done, but the window may still be visible.
* For the client, we want to call onStop()/onStart() to indicate when
* the activity's UI visibility changes.
* @param r Target activity client record.
* @param info Action that will report activity stop to server.
* @param keepShown Flag indicating whether the activity is still shown.
* @param saveState Flag indicating whether the activity state should be saved.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param reason Reason for performing this operation.
*/
private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
boolean saveState, boolean finalStateRequest, 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;
}
if (!finalStateRequest) {
final RuntimeException e = new RuntimeException(
"Performing stop of activity that is already stopped: "
+ r.intent.getComponent().toShortString());
Slog.e(TAG, e.getMessage(), e);
Slog.e(TAG, r.getStateString());
}
}
// One must first be paused before stopped...
//首先必须执行onPause函数
performPauseActivityIfNeeded(r, reason);
if (info != null) {
try {
// First create a thumbnail for the activity...
// For now, don't create the thumbnail here; we are
// doing that by doing a screen snapshot.
info.setDescription(r.activity.onCreateDescription());
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to save state of activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
}
if (!keepShown) {
callActivityOnStop(r, saveState, reason);
}
}
}
/**
* Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
* the client record's state.
* All calls to stop an activity must be done through this method to make sure that
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
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) {
//执行onSaveInstanceState函数
callActivityOnSaveInstanceState(r);
}
try {
//执行onStop函数
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);
}
}
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 {
//调用Instrumentation的callActivityOnSaveInstanceState方法,实际上会调用Activity的onSaveInstanceState方法
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}
/**
* Perform calling of an activity's {@link Activity#onSaveInstanceState}
* method. The default implementation simply calls through to that method.
*
* @param activity The activity being saved.
* @param outState The bundle to pass to the call.
*/
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
//调用Activity的onSaveInstanceState方法
activity.performSaveInstanceState(outState);
}
在performStopActivity函数中通过token从mActivities中获取了一个ActivityClientRecord对象,这个对象就存储了Activity的信息,我们的状态信息就包含在其中。获取到这个ActivityClientRecord之后,调用了performStopActivityInner函数,这个函数又调用了callActivityOnStop这个函数,这个函数大致执行有如下4步:
1、判断是否需要存储Activity状态;
2、如果需要存储Activity状态,调用onSaveInstanceState函数;
3、将状态信息存储到ActivityClientRecord对象的state字段中;
4、调用Activity的Stop方法。
也就是说在执行onStop函数之前,系统会根据情况来选择是否存储Activity的状态,并且将这些状态存储到mActivities中。这个mActivities维护了一个Activity的信息表,当Activity重新启动时,会从mActivities查询对应的ActivityClientRecord,如果这个记录对象中含有状态信息,那么则调用Activity的onResoreInstanceState函数,我们看看相关的代码:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
//执行Activity的OnCreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
//执行Activity的OnRestoreInstanceState方法
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
注:Android9.0把这一个方法拆分成了performLaunchActivity、handleStartActivity两个方法
在调用onRestoreInstanceState之前,系统会判断ActivityClientRecord对象中的state是否为空,如果不为空则说明存储了该Activity的状态,此时就会调用Activity的onRestoreInstanceState函数获取Activity的UI状态信息,然后将这些状态信息传递给Activity的onCreate函数,使得用户可以在onCreate函数中回复以上的状态。
最后贴一下onRestoreInstanceState函数的源码及注释吧:
/**
* This method is called after {@link #onStart} when the activity is
* being re-initialized from a previously saved state, given here in
* <var>savedInstanceState</var>. 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}.
*
* <p>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);
}
}
}