在正常情况下关闭Activity,例如按下返回键、返回箭头等Activity直接就被销毁。但是在Activity内存不足、异常关闭或者屏幕旋转等情况的时候,当前的Activity会被销毁,这个过程中页面上的Fragment、View等信息会被保存下来,当再次展示的时候会被重建,重建后的Activity就会恢复这些数据。一般情况下当系统配置发生改变的时候Activity都会被重建,如果我们不希望当系统配置发生变化界面重建,那么我们需要在AndroidManifest.xml中对Activity的configChange属性进行配置。如下:
android:configChanges="orientation"
当有多个属性时,用|进行分隔。这种情况下这里就不进行讨论,这里主要讨论当没有配置configChanges时候,Activity异常销毁和重建后是如何保存数据和恢复数据的。
一、onSaveInstanceState保存数据入口分析
当Activity异常关闭时候,会回调onSaveInstanceState方法,onSaveInstanceState的调用是处于onPause和onStop之间的,能保证的是onSaveInstanceState方法会在onStop之前调用,但是是否在onPause之前就不一定了。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
在Activity的源码中主要做了三件事情,如下源码标注:
- 存储当前窗口的视图树状态;
- 存储fragment状态;
- 存储生命周期状态。
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);
}
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
//设置生命周期状态
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
二、 存储当前窗口的视图树状态
首先来看如何保持当前的View视图:
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
从上面的源码中可以看出具体的存储是在mWindow中的,而mWindow的实现类是PhoneWindow,因此找到PhoneWindow对应的方法,saveHierarchyState主要做了如下几件事:
- 保存根View;
- 保存当前页面含有焦点的View;
- 保存整个面板内容;
- 保存actionBar信息。
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//1.保存根View
//通过SparseArray来存储数据,太相当于一个key为Integer类型的Map集合
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//调用mContentParent的saveHierarchyState方法,mContentParent为ViewGroup,具体的实现在父View中,而不是在ViewGroup
mContentParent.saveHierarchyState(states);
//将视图树放置到Bundle
outState.putSparseParcelableArray(VIEWS_TAG, states);
//2.保存当前页面含有焦点的View
final View focusedView = mContentParent.findFocus();
//含有焦点的视图必须设置有id否则无法恢复他的焦点状态
if (focusedView != null && focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
//3.保存整个面板内容
SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
savePanelState(panelStates);
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
//4.保存actionBar信息
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
这里保存View通过SparseArray来存储数据,太相当于一个key为Integer类型的Map集合,不难发现这里一共创建了states、panelStates、actionBarStates三个集合分别来存储数据。同时可以看出前页面含有焦点的View没有设置id,则不会被保存。这里重点分析如何保存整个View,在上面源码中:
mContentParent.saveHierarchyState(states);
mContentParent是一个ViewGroup,而ViewGroup中并没该方法,因此可以断定saveHierarchyState方法是其父类View的方法:
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//1、如果当前的View没有设置id则该View的状态不会被存储
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
//2、获取自身状态(默认为空)
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);
//3、将自身状态存放到SparseArray里面,key为id,vaule为自身View的状态
container.put(mID, state);
}
}
}
首先判断如果当前的View没有设置id则该View的状态不会被存储,然后获取自身的Parcelable状态(默认为空),最后将该status保存下来。其实ViewGroup重写了dispatchSaveInstanceState方法,主要是保存自身状态和遍历View保存子View状态。
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//1.保存自身状态
super.dispatchSaveInstanceState(container);
final int count = mChildrenCount;
final android.view.View[] children = mChildren;
//2.遍历View保存子View状态
for (int i = 0; i < count; i++) {
android.view.View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchSaveInstanceState(container);
}
}
}
上面说过获取自身的Parcelable状态(默认为空),这里重点看看如何获取View自身的状态值,看onSaveInstanceState方法:
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
……………………………………………省略若果代码…………………………………………………………………
return BaseSavedState.EMPTY_STATE;
}
这个是获取当前View的默认状态,默认是Empty,在开发中,我们需要存储View的默认状态时候,就需要重写该方法,以TextView为例:来看看TextView中如何重写的:
@Override
public Parcelable onSaveInstanceState() {
//获取空的superState
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
final boolean freezesText = getFreezesText();
boolean hasSelection = false;
int start = -1;
int end = -1;
//保存start和end的文本信息
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
hasSelection = true;
}
}
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;
}
重写完该方法后就会通过dispatchSaveInstanceState中的 container.put(mID, state);添加到集合里面,完成了View状态的存储。
三、存储fragment状态
Parcelable p = mFragments.saveAllState();
在Fragment中onSaveInstanceState是一个空实现。
public Parcelable saveAllState() {
return mHost.mFragmentManager.saveAllState();
}
FragmentManager.saveAllState()才是真正的核心部分。主要分为 3 部分:
- 保存当前管理器Fragment到FragmentState;
- 得到 mAdded 中 Fragment 的 index 列表;
- 保存已经mAdded的Fragment到新的栈内。
Parcelable saveAllState() {
//释放资源
forcePostponedTransactions();
endAnimatingAwayFragments();
execPendingActions();
mStateSaved = true;
mSavedNonConfig = null;
if (mActive == null || mActive.size() <= 0) {
return null;
}
//1.保存当前管理器Fragment到FragmentState中
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) {
//如果 f.mIndex<0,则Fragment没有在栈内,无法被保存
if (f.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + f
+ " has cleared index: " + f.mIndex));
}
haveFragments = true;
//例化一个 FragmentState 并把一些基础属性赋值。
FragmentState fs = new FragmentState(f);
active[i] = fs;
if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
//在该方法中首先触发 onSaveInstanceState 可以让开发者保存一些自定义的属性到 Bundle 中,然
//后调用 saveHierarchyState 方法保存 Fragment 内 View 的状态到 Bundle 中
fs.mSavedFragmentState = saveFragmentBasicState(f);
//如果TAG为空,则设置tag
if (f.mTarget != null) {
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();
}
//保存tag
putFragment(fs.mSavedFragmentState,
FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
if (f.mTargetRequestCode != 0) {
//保存RequestCode
fs.mSavedFragmentState.putInt(
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
f.mTargetRequestCode);
}
}
} else {
//否则直接设置把Fragment数据设置到FragmentState
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;
//2.得到 mAdded 中 Fragment 的 index 列表
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));
}
}
}
//3.保存已经mAdded的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(mBackStack.get(i));
if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+ ": " + 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;
}
四、存储生命周期状态
Activity:
getApplication().dispatchActivitySaveInstanceState(this, outState);
Application:
void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
//获取回调信息数组
Object[] callbacks = collectActivityLifecycleCallbacks();
//遍历回调数组,
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
//保存把outState保存到onActivitySaveInstanceState,他是ActivityLifecycleCallbacks接口的一个方法
//包括很多实现类EmptyActivityLifecycleCallbacks、NfcActivityManager等
((ActivityLifecycleCallbacks)callbacks[i]).onActivitySaveInstanceState(activity,
outState);
}
}
}
主要完成以下:
- 获取回调信息数组集;
- 遍历回调数组,设置生命周期回调。
五、Activity异常关闭时数据的存储总结
上面主要分析了Activity异常关闭时数据的存储的三大部分:
- 存储当前窗口的视图树状态;
- 存储fragment状态;
- 存储生命周期状态。
现在对这三大部分进行总结,绘制了一张图,如下:
六、Activity异常关闭时数据的恢复过程
通过上面的分析得知Activity异常关闭时信息保存在bundle里面,哪么bundle数据有存在那块呢?重建Activity是如何恢复的呢?上面说过调用onSaveInstanceState方法是在onStop方法之前,Activity的onStop方法是在ActivityThread的performStopActivity,省略部分代码,如下:
final void performStopActivity(IBinder token, boolean saveState, String reason) {
//通过Token获取ActivityClientRecord对象,存储了Activity信息
ActivityClientRecord r = mActivities.get(token);
//saveState是否要存储的状态
performStopActivityInner(r, null, false, saveState, reason);
}
private void performStopActivityInner(ActivityClientRecord r,
StopInfo info, boolean keepShown, boolean saveState, String reason) {
if (r != null) {
//判断是否要保存Activity的状态
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
//执行Activity中的onSaveInstanceState
callCallActivityOnSaveInstanceState(r);
}
}
if (!keepShown) {
try {
//执行Activity中的onStop
r.activity.performStop(false /*preserveWindow*/);
} catch (Exception e) {
}
}
}
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 {
//实际上会调用Activity中的onSaveInstanceState
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}
在通过performStopActivity中通过Token获取ActivityClientRecord对象,存储了Activity信息,之后调用了callCallActivityOnSaveInstanceState方法.再启动Activity的时候再将bundle传递到Activity的onCreate方法。如下:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
/………………………………………………………………省略代码…………………………………………………………………………/
//传递给Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
/………………………………………………………………省略代码…………………………………………………………………………/
//传递给Activity的onRestoreInstanceState方法
if (!r.activity.mFinished) {
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);
}
}
/………………………………………………………………省略代码…………………………………………………………………………/
}
这样Activity重启的时候就能够读取到保存的信息了,很好的提升了用户体验。