可能大部分的安卓开发者对于onSaveInstanceState和onRestoreInstanceState是有熟悉又陌生,熟悉吧是因为经常听到,陌生吧,是因为平时用的少,只知道是保存和恢复Activity状态的,其他的再多也不知道了,那本文将在Android 11的源码基础上来分析4个问题。
-
onSaveInstanceState做了什么
-
onSaveInstanceState什么时候执行
-
onRestoreInstanceState做了什么
-
onRestoreInstanceState什么时候执行
1.onSaveInstanceState做了什么
android.app.Activity
protected void onSaveInstanceState(@NonNull Bundle outState) {
1.保存当前UI状态
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
2.保存Fragments状态
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
dispatchActivitySaveInstanceState(outState);
}
复制代码
mWindow是Window类型,Window是抽象类,唯一实现类是PhoneWindow,在Activity启动的时候,会执行Activity的attach方法,内部进行赋值
android.app.Activity
final void attach(...){
......
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
......
}
复制代码
· 稍微提一嘴,留意一下mWindow.setCallback(this);这行代码,感兴趣的可以自己去探索一下,之后的谋篇文章里,我应该也会提到这一句。
1.1saveHierarchyState
om.android.internal.policy.PhoneWindow
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
使用SparseArray存储
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent其实就是setContentParent设置进来的View
mContentParent.saveHierarchyState(states);
将视图结构保存进outState
outState.putSparseParcelableArray(VIEWS_TAG, states);
// Save the focused view ID.
根据注释理解为保存获取了焦点的View
final View focusedView = mContentParent.findFocus();
if (focusedView != null && focusedView.getId() != View.NO_ID) {
//只保存设置了ID的View
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
// save the panels
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;
}
复制代码
重点关注一下mContentParent.saveHierarchyState(states); 方法
android.view.View
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
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没有Id,则不会保存状态,②通过 onSaveInstanceState() 获取自身状态,如果自身的状态不为null,则在③处保存状态。而onSaveInstanceState默认返回的空状态
@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
......
return BaseSavedState.EMPTY_STATE;
}
复制代码
所以如果想保存View的状态你需要覆写onSaveInstanceState方法。
其实还有个会疑问,我们setContentView的必须是一个ViewGroup的类型啊,这个View的方法似乎满足不了我们的需求,所以我们去看看ViewGroup中是否复写了该方法。
android.view.ViewGroup
@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方法,目的是保存自身的状态,其次循环自己的子View,执行子View的dispatchSaveInstanceState,如果子View任然是ViewGroup则再次执行改流程。和分析View的事件分发流程似乎有一丢丢相似。
onSaveInstanceState什么时候执行
网上大部分的文章对于onSaveInstanceState的描述基本上是打印几个生命周期的日志,然后告诉结论:是在onPause之后,在onStop之前调用。深入些的会在源码中找到依据,那到底是不是这样呢?
android.app.Activity
/**
* The hook for {@link ActivityThread} to save the state of this activity.
*
* Calls {@link #onSaveInstanceState(android.os.Bundle)}
* and {@link #saveManagedDialogs(android.os.Bundle)}.
*
* @param outState The bundle to save the state to.
* @param outPersistentState The bundle to save persistent state to.
*/
final void performSaveInstanceState(@NonNull Bundle outState,
@NonNull PersistableBundle outPersistentState) {
dispatchActivityPreSaveInstanceState(outState);
//保存状态
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
", " + outPersistentState);
dispatchActivityPostSaveInstanceState(outState);
}
复制代码
Activity调用onSaveInstanceState的方法是在上面代码中,根据方法的注释,懂的人就知道直接去ActivityThread中找调用的地方了,但是却搜不到?别着急,在之前的文章中多次提到Instrumentation会管理Activity的生命周期,所以performSaveInstanceState的调用其实是在Instrumentation中。
android.app.Instrumentation
public void callActivityOnSaveInstanceState(@NonNull Activity activity,
@NonNull Bundle outState) {
activity.performSaveInstanceState(outState);
}
复制代码
那这又是在什么地方调用的呢?看过之前文章的小伙伴,可能会有一种印象,Instrumentation*在ActivityThread中有个变量mInstrumentation,所以猜测大概率也是在那调用的。不过这次直接在通过AS点击方法能直接跳到调用的地方。确实是在ActivityThread中
android.app.ActivityThread
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);
}
}
复制代码
所以最后还是由ActivityThread去控制Instrumentation执行Activity的performSaveInstanceState方法。那现在只需要找到:ActivityThread中的callActivityOnSaveInstanceState是什么时候执行的,就可以知道问题onSaveInstanceState什么时候调用的答案。
我是用AS看源码的,所以只需要双击callActivityOnSaveInstanceState方法,用快捷键Alt+F7就可以找到方法使用的地方了。
由图可知调用了三次,分别在执行onPause和onStop的方法的时候,具体调用顺序还需要在细看。虽然这两个方法上层还有几次调用,但最后都是在TransactionExecutor类下的performLifecycleSequence方法中发起。对这快有兴趣的可以看看Android 11源码分析: Activity的启动流程
这这这……不是说在onPause之后,在onStop之前么。怎么还有三处调用呢?这可不是我下的结论,我的结论还没说呢,继续跟踪找答案。
先看第一次调用的地方
android.app.ActivityThread
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
PendingTransactionActions pendingActions) {
......
// Pre-Honeycomb apps always save their state before pausing
final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
if (shouldSaveState) {
关键代码
callActivityOnSaveInstanceState(r);
}
内部通过Instrumentation,调用Acivity的performPause
方法,进而执行onPause
performPauseActivityIfNeeded(r, reason);
......
}
复制代码
-
在performPauseActivity方法中,shouldSaveState为true,则会执行onSaveInstanceState,
-
当r.activity.mFinished为false,且r.isPreHoneycomb() 为true的时候shouldSaveState为true
mFinished是Activity的全局变量,只有在执行生命周期finish的时候,才为true。isPreHoneycomb()的代码如下
android.app.ActivityThread
private boolean isPreHoneycomb() {
return activity != null && activity.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.HONEYCOMB;
}
复制代码
HONEYCOMB常量11,也就是SDK11(Android 3.0)
结论1:当Activity没有finish生命周期,且当前版本在安卓3.0之前的,OnSaveInstanceState方法在onPause之前调用
现在基本上没有安卓3.0的设备了吧。
后面两次调用都在callActivityOnStop方法内。一个方法里,执行两次OnSaveInstanceState又是啥原因呢?
android.app.ActivityThread
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 {
执行onStop
r.activity.performStop(r.mPreserveWindow, 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);
}
}
复制代码
这块代码很有意思,第二处执行在onStop之前,第三处在onStop之后。且执行条件区别就在于这个isPreP。
android.app.ActivityThread
private boolean isPreP() {
return activity != null && activity.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.P;
}
复制代码
Android P 也就是SDK 28 安卓9
结论已经很明显了,再加上方法的前面两行给的注释
Before P onSaveInstanceState was called before onStop, starting with P it’s
called after. Before Honeycomb state was always saved before onPause.
结论2:安卓3.0之后至9.0之前,OnSaveInstanceState方法在onPause之后onStop之前调用这个也是目前网上绝大部分文章的结论。
结论3:安卓9.0之后OnSaveInstanceState方法在onStop之后调用
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
复制代码
再反过头来看shouldSaveState变量,他也很重要,如果他为false,则无论什么版本都不会执行OnSaveInstanceState方法。我们都知道,只有未经你许可Activity的时候,我们才会需要保存数据,并不是每次都会执行OnSaveInstanceState方法的。那可想而知,shouldSaveState就是什么叫未经你许可的体现了。
从后往前看:
-
!isPreHoneycomb() : 大于安卓3.0
-
r.state == null : state是ActivityClientRecord下的Bundle类型,也是后面onRestoreInstanceState需要恢复的数据。简而言之:没有保存过数据,则进行保存,保存过则不需要再次保存
-
!r.activity.mFinished: 之前讲过
-
saveState:这个变量是关键。设计到的case有点多,如果再次进入细节分析篇幅过程且同时我也还没想好怎么去分析。所以先列出一些实际情况为true的操作。 以下描述参考《Android开发艺术探索》
· 当用户按下HOME键时
· 长按HOME键,选择运行其他的程序时
· 按下电源按键(关闭屏幕显示)时 > · 从activity A中启动一个新的activity时
. 屏幕方向切换
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。
onRestoreInstanceState做了什么
相对于onSaveInstanceState来说onRestoreInstanceState就简单不少了,一个保存,那另一个就是恢复了。
android.app.Activity
public void onRestoreInstanceState(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
if (savedInstanceState != null) {
onRestoreInstanceState(savedInstanceState);
}
}
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
关键代码
mWindow.restoreHierarchyState(windowState);
}
}
}
复制代码
不用说也知道,前面费劲心思保存的Bundle类型的outState,就是onRestoreInstanceState需要恢复的windowState。具体执行也是在PhonewWindow下的restoreHierarchyState中,其实就是把之前保存的操作,反向在恢复一次。感兴趣的同学可以自行去了解一下。
onRestoreInstanceState什么时候执行
onRestoreInstanceState的执行时机,其实没什么争议,就是在onStart之前,但是我们还是自己去探索一下吧,这样放心。
android.app.Activity
/**
* The hook for {@link ActivityThread} to restore the state of this activity.
*
* Calls {@link #onSaveInstanceState(android.os.Bundle)} and
* {@link #restoreManagedDialogs(android.os.Bundle)}.
*
* @param savedInstanceState contains the saved state
* @param persistentState contains the persistable saved state
*/
final void performRestoreInstanceState(@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
执行
onRestoreInstanceState(savedInstanceState, persistentState);
if (savedInstanceState != null) {
restoreManagedDialogs(savedInstanceState);
}
}
复制代码
可以看到,onRestoreInstanceState方法在performRestoreInstanceState中调用,根据熟悉的注释,我们这次学乖了,直接去Instrumentation类里找,肯定又是包装了一层在给ActivityThread用。
android.app.Instrumentation
public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
@Nullable Bundle savedInstanceState,
@Nullable PersistableBundle persistentState) {
调用activity的performRestoreInstanceState
activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
复制代码
果不其然
@Override
public void handleStartActivity(IBinder token, PendingTransactionActions pendingActions) {
......
①
// Start
activity.performStart("handleStartActivity");
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);
}
}
......
}
复制代码
上面代码中,官方的两个简单的注释,已经很明白了。执行OnRestoreInstanceState方法必然是在Activity的onStart之后。
总结
- OnSaveInstanceState会保存当前页面的UI状态,用户可以将需要保存的数据也写进outState中
- 在安卓3.0版本以前,OnSaveInstanceState方法在onPause之前执行
- 在安卓3.0至安卓9.0版本中,如果是未经许可时销毁则OnSaveInstanceState方法在onPause之后,onStop之前执行
- 在安卓9.0之后,如果是未经许可时销毁OnSaveInstanceState方在onStop之后执行
- OnRestoreInstanceState会将进行视图恢复,同时提供用户之前保存的数据
- Activity重建后,OnRestoreInstanceState会在onStart之后执行
- OnSaveInstanceState和onRestoreInstanceState是设计模式中备忘录模式的体现