Activity状态保存与恢复
之前没太明白视图在什么时候可以保存状态,在什么时候状态可以被恢复,想了想还是得琢磨琢磨源码,虽然干的活离Android越来越远,但毕竟老本行,还是需要花精力琢磨的。
在不同的版本中,Activity保存状态的时机是不一样的,在Android 3之前,把Activity状态的保存是在onPause之前被调用onSaveInstanceState(ActivityThread#performPauseActivity),而在Android 9之前,把Activity状态的保存后置到了onStop之前,在Android 9之后,把Activity的状态保存后置到了onStop之后(ActivityThread#callActivityOnStop)。
而通过前面的分析:
Android Activity启动流程
Android UI工作流程1
Android UI工作流程2
我们可以知道在AMS创建Activity的时候,通过Binder事务,调用到客户端进程,执行LaunchActivityItem#execute,其中就包括ActivityClientRecord的创建。
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(..., mState,...);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
而一个ActivityClientRecord代表着用户进程的一个Activity实例记录。其中的Bundle类型的mState字段就代表着这个Activity的状态。
回看到ActivityThread#callActivityOnStop,最终的逻辑都是调用到ActivityThread#callActivityOnSaveInstanceState方法:
private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
// 给ActivityClientRecord#state创建一个新的Bundle对象。
r.state = new Bundle();
r.state.setAllowFds(false);// 序列化底层对于是否支持文件描述符的逻辑是两套,这里设置为false,因为这个bundle是不需要保存fd的
if (r.isPersistable()) { // 如果是系统内置的应用
r.persistentState = new PersistableBundle();
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
r.persistentState);
} else {
// 对于普通App而言,通过下面这里调用保存状态
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}
-> Instrumentation#callActivityOnSaveInstanceState
-> activity.performSaveInstanceState
final void performSaveInstanceState(@NonNull Bundle outState) {
dispatchActivityPreSaveInstanceState(outState);// 可以注册ActivityLifecycleCallbacks给Application获取这个hook回调。
onSaveInstanceState(outState);// 执行Activity实例的onSaveInstanceState方法。
saveManagedDialogs(outState);// 保存关联了当前Activity的Dialog状态。
mActivityTransitionState.saveState(outState);// 保存Activity转场动画相关的状态
storeHasCurrentPermissionRequest(outState);// 保存当前Activity是否正在申请权限
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
dispatchActivityPostSaveInstanceState(outState);// 和上面的pre类似,也是分发一个hook回调。
}
那么核心就是Activity的onSaveInstanceState,我们知道,我们可以通过重写这个方法来实现一些额外的状态保存的逻辑,当然即便我们不重写Activity实例的onSaveInstanceState方法,也有一些Activity的默认逻辑,默认的实现是:
protected void onSaveInstanceState(@NonNull Bundle outState) {
// 核心通过Window的saveHierarchyState保存视图状态。
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
// 内部通过FragmentManager保存Activity持有的所有Fragment状态
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);
}
View的状态保存和恢复:
View的状态保存:
而Window实例自然就是PhoneWindow:
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
// 如果Window没有内容,就无需保存什么
// 而mContentParent其实就是Activity结构中的R.id.content的FrameLayout
if (mContentParent == null) {
return outState;
}
// 创建一个稀松数组结构的散列表
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
// 见下文:
mContentParent.saveHierarchyState(states);
// 把视图树的状态保存结果放到Activity传入的Bundle中
outState.putSparseParcelableArray(VIEWS_TAG, states);
// 保存当前正获取到焦点的View的id
final View focusedView = mContentParent.findFocus();
if (focusedView != null && focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
// ...
return outState;
}
mContentParent.saveHierarchyState(states),实现就是View啦:
// View.dispatchSaveInstanceState实现如下:
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
// 只要view有id,并且没有关闭掉状态保存功能,就会被保存
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
// 回调View的onSaveInstanceState实现
// 在View.java中的实现其实就是返回一个空的Parcelable对象(和一些Autofill相关的逻辑)
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// 在上面创建的稀松数组中,存放的key就是View的id。
container.put(mID, state);
}
}
}
所以可以看到,对于View而言,其实就是保存自身的状态,而VierGroup则不难猜到,肯定是遍历子View进行状态保存:
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);
}
}
}
如此以来,视图树的状态就保存完了,我们可以看一下比如Switch这个开关组件的状态保存,因为Switch继承自CompoundButton,其并没有实现自己的onSaveInstanceState方法,在父类CompoundButton中:
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.checked = isChecked();// 保存按钮的checked选中态。
return ss;
}
View的状态恢复:
那视图的状态保存完了,接下来就是考虑在恰当的时候进行恢复,还是以Switch开关这个控件为例子:
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// 恢复是否被选中的状态。
setChecked(ss.checked);
requestLayout();
}
而在Activity进行Start的时候,会主动回调到相关逻辑:
-> Activity#handleStartActivity
-> Instrumentation#callActivityOnRestoreInstanceState
-> activity.performRestoreInstanceState
final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
// 回调onRestoreInstanceState
onRestoreInstanceState(savedInstanceState);
// 恢复Dialog的状态
restoreManagedDialogs(savedInstanceState);
}
视图状态的保存开始自PhoneWindow,恢复自然也一样,在onRestoreInstanceState的默认实现中,先获取到之前保存的Window状态的Bundle对象,然后mWindow.restoreHierarchyState进行恢复,其实和序列化的过程很类似,状态的恢复其实就是状态保存过程的逆向,视图树状态的恢复关键代码:
mContentParent.restoreHierarchyState(savedStates);
mContentParent是FrameLayout实例,其本身没有实现restoreHierarchyState,在View中:
-> View#restoreHierarchyState
-> View#dispatchRestoreInstanceState
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);
// 如果有当前View的状态
if (state != null) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
// 有的话调用onRestoreInstanceState
onRestoreInstanceState(state);
}
}
}
onRestoreInstanceState在子View中的实现就如上面Switch的实现。
Fragment状态的保存与恢复:
Fragment状态保存:
在Activity的onSaveInstanceState中,我们看到了Fragment状态相关的保存逻辑:
-> mFragments.saveAllState()
-> mHost.mFragmentManager.saveAllState()
FragmentManagerImpl的实现如下:
Parcelable saveAllState() {
// 强制执行一些前置任务,比如提交的事务全部执行掉,以此保证状态是最新的。
// 嗅到了一些不好的味道
forcePostponedTransactions();
endAnimatingAwayFragments();
execPendingActions();
mStateSaved = true;
mSavedNonConfig = null;
// 如果当前没有活跃状态的Fragment,就不保存
if (mActive == null || mActive.size() <= 0) {
return null;
}
// First collect all active fragments.
int N = mActive.size();
FragmentState[] active = new FragmentState[N];
boolean haveFragments = false;
for (int i=0; i<N; i++) {
Fragment f = mActive.valueAt(i);
if (f != null) {
if (f.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + f
+ " has cleared index: " + f.mIndex));
}
haveFragments = true;
FragmentState fs = new FragmentState(f);
active[i] = fs;
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
// 保存Fragment对象的状态
// 在这其中主要做两件事,第一个是调用Fragment自身的onSave方法保存自身的状态
// 第二个是调用Fragment的mChildFragmentManager去递归保存子Fragment的状态。
fs.mSavedFragmentState = saveFragmentBasicState(f);
if (f.mTarget != null) {
// 保存Fragment的TargetFragment的在mActive中记录的索引。
if (f.mTarget.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: " + f
+ " has target not in fragment manager: " + f.mTarget));
}
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;
}
if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
+ fs.mSavedFragmentState);
}
}
if (!haveFragments) {
if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
return null;
}
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;
if (added[i] < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + mAdded.get(i)
+ " has cleared index: " + added[i]));
}
if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+ ": " + mAdded.get(i));
}
}
// 保存Fragment的回退栈的状态
if (mBackStack != null) {
N = mBackStack.size();
if (N > 0) {
backStack = new BackStackState[N];
for (int i=0; i<N; i++) {
backStack[i] = new BackStackState(this, mBackStack.get(i));
if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+ ": " + mBackStack.get(i));
}
}
}
// 创建Fragment状态对象,并将上面收集到的所有的状态数据全部写入到fms中,然后交给Activity保存。
FragmentManagerState fms = new FragmentManagerState();
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
fms.mNextFragmentIndex = mNextFragmentIndex;
if (mPrimaryNav != null) {
fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
}
saveNonConfig();
return fms;
}
其实从上面代码,尤其是活跃状态Fragment状态的保存那一块,也可以看出,Fragment的状态管理确实很复杂。。。
除了Activity中调用FM进行Fragment的状态保存,FragmentManager还提供了public方法: saveFragmentInstanceState(fragment), 可以对单个Fragment进行状态保存。
以及在FragmentManager的moveToState进行Fragment状态变更的时候,当Fragment的状态回退到ACTIVITY_CREATE之前,其实也就是初始化状态(Fragment执行onDestroy之前会置为这一状态)和不合法状态。
Fragment状态恢复:
按照Google的编码习惯,状态的恢复也一定是在FragmentManagerImpl中,并且api叫做restoreAllState,并且和上文的三种状态保存是对应的,API设计的很合理。
而Activity中对于Fragment的状态恢复入口在Acitvity的onCreate中:
if (savedInstanceState != null) {
// ...
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
// 调用到Fragment的restoreAllState方法。
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
restoreAllState方法主要分为一下几步:
- 先把受Activity重建影响的Fragment实例状态进行恢复。
- 把所有之前位于mActive中的Fragment实例进行重新创建实例和状态恢复。
- 更新Fragment的mTarget索引。
- 把上面搞出来的一堆Fragment添加到FragmentManager中的mAdded容器中。
- 如有需要的话,恢复Fragment的返回栈状态。
而对于外部主动使用saveFragmentInstanceState进行状态保存的方法,对应的恢复状态方法是Fragment的setInitialSavedState,这个方法其实就是把状态对象赋值给Fragment,而后FragmentManager执行到Fragment对应的声明周期的时候,会主动去把Fragment已经存在的状态对象传递给对应的方法比如onCreateView。
对于通过moveToState触发的状态恢复,会在Fragment的newState > Fragment.CREATED的时候,触发视图的恢复,而保存状态的Bundle也会在onCreateView,onViewCreated,onActivityCreated中传递给Fragment。以及通过FragmentLifecycleCallbacks暴露给外部。在之后是调用Fragment的restoreViewState,进行视图状态的恢复,开始的起点就是Fragment的mView字段,而这个字段,就是我们通过onCreateView返回的对象,当Fragment通过onCreateView完成Fragment视图的创建之后,fm会将其赋值给Fragment的mView字段。
Dialog状态的保存和恢复:
Dialog状态的保存:
看Activity.java中这一部分的源码的提交记录已经是2009年了,相关的逻辑其实就是调用当前Activity的所有Dialog实例,一次进行保存和恢复,相关的逻辑官方推荐是用DialogFragment来弹Dialog,不妨看下DialogFragment的实现(DialogFragment本身也是一个Fragment实例,通过FragmentManager进行管理,所以状态的保存和恢复和Fragment没什么太大的差异,只不过DialogFragment实现了自定义的onSaveInstanceState方法):
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mDialog != null) {
// 调用当前DialogFragment关联的mDialog的onSaveInstanceState方法
Bundle dialogState = mDialog.onSaveInstanceState();
if (dialogState != null) {
outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
}
}
if (mStyle != STYLE_NORMAL) {
outState.putInt(SAVED_STYLE, mStyle);
}
if (mTheme != 0) {
outState.putInt(SAVED_THEME, mTheme);
}
if (!mCancelable) {
outState.putBoolean(SAVED_CANCELABLE, mCancelable);
}
// 如果当前DialogFragment正在展示Dialog,要把这个标记位记录下来
if (!mShowsDialog) {
outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
}
if (mBackStackId != -1) {
outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
}
}
Dialog中的实现和其它组件相比比较简单,就是是否正在展示dialog的标记位保存和视图状态的保存:
public @NonNull Bundle onSaveInstanceState() {
Bundle bundle = new Bundle();
// 保存当前Dialog是否正在展示态
bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
if (mCreated) {
// Dialog关联的PhoneWindow视图状态保存
bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState());
}
return bundle;
}
状态的恢复如下:
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
if (dialogHierarchyState == null) {
// dialog has never been shown, or onCreated, nothing to restore.
return;
}
// 如果之前Dialog还没有执行到onCreate,执行一下onCreate。
dispatchOnCreate(savedInstanceState);
// 恢复Dialog关联的视图树状态。
mWindow.restoreHierarchyState(dialogHierarchyState);
// 如果之前Dialog是正在展示,这里重新show出来
if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
show();
}
}
ull) {
// dialog has never been shown, or onCreated, nothing to restore.
return;
}
// 如果之前Dialog还没有执行到onCreate,执行一下onCreate。
dispatchOnCreate(savedInstanceState);
// 恢复Dialog关联的视图树状态。
mWindow.restoreHierarchyState(dialogHierarchyState);
// 如果之前Dialog是正在展示,这里重新show出来
if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
show();
}
}
最后
在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。
**如果你觉得自己学习效率低,缺乏正确的指导,可以扫码,加入我们资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。