作者:李尚
之前已经说过,OnRestoreInstanceState虽然与onSaveInstanceState是配对实现的,但是其调用却并非完全成对的,在Activity跳转或者返回主界面时,onSaveInstanceState是一定会调用的,但是OnRestoreInstanceState却不会,它只有Activity或者App被异常杀死,走恢复流程的时候才会被调用。如果没有被异常杀死,不走Activity的恢复新建流程,也就不会回调OnRestoreInstanceState,简单看一下Activity的加载流程图:
可以看出,OnRestoreInstanceState的调用时机是在onStart之后,在onPostCreate之前。那么正常的创建为什么没调用呢?看一下ActivityThread中启动Activity的源码:
private Activity performLaunchActivity(Activi
...
mInstrumentation.callActivityOnCreate(activity, r.state);
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
}
可以看出,只有r.state != null的时候,才通过mInstrumentation.callActivityOnRestoreInstanceState回调OnRestoreInstanceState,r.state就是ActivityManagerService通过Binder传给ActivityThread数据,主要用来做场景恢复。以上就是onSaveInstanceState与OnRestoreInstance执行时机的一些分析。下面结合具体的系统View控件来分析一下这两个函数的具体应用:比如ViewPager与FragmentTabHost,这两个空间是主界面最常用的控件,内部对后台杀死做了兼容,这也是为什么被杀死后,Viewpager在恢复后,能自动定位到上次浏览的位置。
首先看一下ViewPager做的兼容,ViewPager在后台杀死的情况下,仍然能恢复到上次关闭的位置,这也是对体验的一种优化,这其中的原理是什么?之前分析onSaveInstanceState与onRestoreInstanceState的时候,只关注了Fragment的处理,其实还有一些针对Window窗口及Vie的处理,先看一下onSaveInstanceState针对窗口保存了什么:
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
}
@Override
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
View focusedView = mContentParent.findFocus();
...
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
// save the panels
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
if (mActionBar != null) {
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
Window其实就是PhonwWinow,saveHierarchyState其实就是针对当前窗口中的View保存一些场景信息 ,比如:当前获取焦点的View的id、ActionBar、View的一些状态,当然saveHierarchyState递归遍历所有子View,保存所有需要保存的状态:
ViewGroup.java
@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);
}
}
}
可见,该函数首先通过super.dispatchSaveInstanceState保存自身的状态,再递归传递给子View。onSaveInstanceState主要用于获取View需要保存的State,并将自身的ID作为Key,存储到SparseArray states列表中,其实就PhoneWindow的一个列表,这些数据最后会通过Binder保存到ActivityManagerService中去
View.java
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) {
container.put(mID, state);
}
}
}
那么针对ViewPager到底存储了什么信息?通过下面的代码很容易看出,其实就是新建个了一个SavedState场景数据,并且将当前的位置mCurItem存进去。
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.position = mCurItem;
if (mAdapter != null) {
ss.adapterState = mAdapter.saveState();
}
return ss;
}
到这里存储的事情基本就完成了。接下来看一下ViewPager的恢复以及onRestoreInstanceState到底做了什么,
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
从代码可以看出,其实就是获取当时保存的窗口信息,之后通过mWindow.restoreHierarchyState做数据恢复,
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
mContentParent.restoreHierarchyState(savedStates);
}
...
if (mActionBar != null) {
...
mActionBar.restoreHierarchyState(actionBarStates);
}
}
对于ViewPager会发生什么?从源码很容易看出,其实就是取出SavedState,并获取到异常杀死的时候的位置,以便后续的恢复,
ViewPager.java
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
if (mAdapter != null) {
mAdapter.restoreState(ss.adapterState, ss.loader);
setCurrentItemInternal(ss.position, false, true);
} else {
mRestoredCurItem = ss.position;
mRestoredAdapterState = ss.adapterState;
mRestoredClassLoader = ss.loader;
}
}
以上就解释了ViewPager是如何通过onSaveInstanceState与onRestoreInstanceState保存、恢复现场的。如果是ViewPager+FragmentAdapter的使用方式,就同时涉及FragmentActivity的恢复、也牵扯到Viewpager的恢复,其实FragmentAdapter也同样针对后台杀死做了一些兼容,防止重复新建Fragment,看一下FragmentAdapter的源码:
FragmentPagerAdapter.java
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
<!--是否已经新建了Fragment??-->
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
1 如果Activity中存在相应Tag的Fragment,就不要通过getItem新建
if (fragment != null) {
mCurTransaction.attach(fragment);
} else {
2 如果Activity中不存在相应Tag的Fragment,就需要通过getItem新建
fragment = getItem(position);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
FragmentCompat.setMenuVisibility(fragment, false);
FragmentCompat.setUserVisibleHint(fragment, false);
}
return fragment;
}
从1与2 可以看出,通过后台恢复,在FragmentActivity的onCreate函数中,会重建Fragment列表,那些被重建的Fragment不会再次通过getItem再次创建,再来看一下相似的控件FragmentTabHost,FragmentTabHost也是主页常用的控件,FragmentTabHost也有相应的后台杀死处理机制,从名字就能看出,这个是专门针对Fragment才创建出来的控件。
FragmentTabHost其实跟ViewPager很相似,在onSaveInstanceState执行的时候保存当前位置,并在onRestoreInstanceState恢复postion,并重新赋值给Tabhost,之后FragmentTabHost在onAttachedToWindow时,就可以根据恢复的postion设置当前位置,代码如下:
FragmentTabHost.java
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.curTab = getCurrentTabTag();
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setCurrentTabByTag(ss.curTab);
}
在FragmentTabHost执行onAttachedToWindow时候,会首先getCurrentTabTag ,如果是经历了后台杀死,这里得到的值其实是恢复的SavedState里的值,之后通过doTabChanged切换到响应的Tab,注意这里切换的时候,Fragment由于已经重建了,是不会再次新建的。
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
String currentTab = getCurrentTabTag();
...
ft = doTabChanged(currentTab, ft);
if (ft != null) {
ft.commit();
mFragmentManager.executePendingTransactions();
}
}
比如:针对FragmentActivity ,不重建:
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
savedInstanceState.putParcelable(“android:support:fragments”, null);}
super.onCreate(savedInstanceState);
}
如果是系统的Actvity改成是“android:fragments",不过这里需要注意:对于ViewPager跟FragmentTabHost不需要额外处理,处理了可能反而有反作用。
针对Window,如果不想让View使用恢复逻辑,在基类的FragmentActivity中覆盖onRestoreInstanceState函数即可。
protected void onRestoreInstanceState(Bundle savedInstanceState) {
}
当然以上的做法都是比较粗暴的做法,最好还是顺着Android的设计,在需要保存现场的地方保存,在需要恢复的地方,去除相应的数据进行恢复。以上就是后台杀死针对FragmentActivity、onSaveInstanceState、onRestoreInstanceState的一些分析,后面会有两篇针对后台杀死原理,以及ActivityManagerService如何处理杀死及恢复的文章。
仅供参考,欢迎纠错指正
Android后台杀死系列之二:ActivityManagerService与App现场恢复机制 (主要讲述AMS如何为App恢复现场的原理) 这篇有用些
Android后台杀死系列之三:LowMemoryKiller原理(4.3-6.0)(主要讲述App被后台杀死的原理)
Fragment Transactions & Activity State Loss精
Lowmemorykiller笔记 精
Fragment实例化,Fragment生命周期源码分析
android.app.Fragment$InstantiationException的原因分析
Android Framework架构浅析之【近期任务】
Android Low Memory Killer介绍
Android开发之InstanceState详解
Square:从今天开始抛弃Fragment吧!
对Android近期任务列表(Recent Applications)的简单分析
Android——内存管理-lowmemorykiller 机制
ActivityStackSupervisor分析
A Deeper Look of ViewPager and FragmentStatePagerAdaper
View的onSaveInstanceState和onRestoreInstanceState过程分析
相关阅读:Android后台杀死系列之一:FragmentActivity及PhoneWindow后台杀死处理机制(上篇)
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者李尚授权发布。