Android后臺殺死系列之一:FragmentActivity及PhoneWindow后臺殺死處理機制
App在后臺久置后,再次從桌面或最近的任務列表喚醒時經常會發生崩潰,這往往是App在后臺被系統殺死,再次恢復的時候遇到了問題,而在使用FragmentActivity+Fragment的時候會更加頻繁。比如,如果Fragment沒有提供默認構造方法,就會在重建的時候因為反射創建Fragment失敗而崩潰,再比如,在onCreate里面new 一個FragmentDialog,并且show,被后臺殺死后,再次喚醒的時候,就會show兩個對話框,這是為什么?其實這就涉及了后臺殺死及恢復的機制,其中涉及的知識點主要是FragmentActivity、ActivityManagerService、LowMemoryKiller機制、ActivityStack、Binder等一系列知識點。放在一篇文章里面可能會有些長,因此,Android后臺殺死系列寫了三篇:
- 開篇:FragmentActivity及PhoneWindow后臺殺死處理機制
- 原理篇1:后臺殺死與LowmemoryKiller(主要講述App被后臺殺死的原理)
- 原理篇2:后臺殺死與App現場恢復(主要講述AMS如何為App恢復現場的原理)
本篇是Android后臺殺死系列的第一篇,主要講解在開發過程中,由于后臺殺死涉及的一些崩潰,以及如何避免這些崩潰,還有就是簡單的介紹一下onSaveInstanceState與onRestoreInstanceState執行時機與原理,這兩個函數也是Android面試時常問的兩個點,是比簡單的啟動模式Activity聲明周期稍微更深入細致一些的地方,也通過這個點引入后臺殺死及恢復原理。
FragmentActivity被后臺殺死后恢復邏輯
當App被后臺異常殺死后,再次點擊icon,或者從最近任務列表進入的時候,系統會幫助恢復當時的場景,重新創建Activity,對于FragmentActivity,由于其中有Framgent,邏輯會相對再復雜一些,系統會首先重建被銷毀的Fragment。
舉個栗子
我們創建一個Activity,并且在onCreate函數中新建并show一個DialogFragment,之后通過某種方式將APP異常殺死(RogueKiller模擬后臺殺死工具),再次從最近的任務喚起App的時候,會發現顯示了兩個DialogFragment,代碼如下:
- public class DialogFragmentActivity extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- DialogFragment dialogFragment = new FragmentDlg();
- dialogFragment.show(getSupportFragmentManager(), "");
- }
這不僅讓我們奇怪,為什么呢?雖然被殺死了,但是onCreate函數在執行的時候還是只執行了一次啊,為什么會出現兩個DialogFragment,這里其實就有一個DialogFragment是通過Android自身的恢復重建機制重建出來,在異常殺死的情況下onCreate(Bundle savedInstanceState)函數的savedInstanceState參數也不是null,而是包含了被殺死時所保存的場景信息。再來看個崩潰的例子,新建一個CrashFragment,并且丟棄默認無參構造方法:
- public class CrashFragment extends Fragment {
- public CrashFragment(String tag) {
- super();
- }
- }
之后再Activity中Add或replace添加這個CrashFragment,在CrashFragment顯示后,通過RogueKiller模擬后臺殺死工具模擬后臺殺死,再次從最近任務列表里喚起App的時候,就會遇到崩潰,
- Caused by: android.support.v4.app.Fragment$InstantiationException:
- Unable to instantiate fragment xxx.CrashFragment:
- make sure class name exists, is public, and has an empty constructor that is public
- at android.support.v4.app.Fragment.instantiate(Fragment.java:431)
- at android.support.v4.app.FragmentState.instantiate(Fragment.java:102)
- at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1952)
- at android.support.v4.app.FragmentController.restoreAllState(FragmentController.java:144)
- at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:307)
- at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:81)
上面的這兩個問題主要涉及后臺殺死后FragmentActivity自身的恢復機制,其實super.onCreate(savedInstanceState)在恢復時做了很多我們沒有看到的事情,先看一下崩潰:
為什么Fragment沒有無參構造方法會引發崩潰
看一下support-V4中FragmentActivity中onCreate代碼如下:
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- mFragments.attachHost(null /*parent*/);
- super.onCreate(savedInstanceState);
- ...
- if (savedInstanceState != null) {
- Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
- mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
- }
- mFragments.dispatchCreate();
- }
可以看到如果savedInstanceState != null,就會執行mFragments.restoreAllState邏輯,其實這里就牽扯到恢復時重建邏輯,再被后臺異常殺死前,或者說在Activity的onStop執行前,Activity的現場以及Fragment的現場都是已經被保存過的,其實是被保存早ActivityManagerService中,保存的格式FragmentState,重建的時候,會采用反射機制重新創Fragment
- void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
- ...
- for (int i=0; i<fms.mActive.length; i++) {
- FragmentState fs = fms.mActive[i];
- if (fs != null) {
- Fragment f = fs.instantiate(mHost, mParent);
- mActive.add(f);
- ...
其實就是調用FragmentState的instantiate,進而調用Fragment的instantiate,最后通過反射,構建Fragment,也就是,被加到FragmentActivity的Fragment在恢復的時候,會被自動創建,并且采用Fragment的默認無參構造方法,如果沒喲這個方法,就會拋出InstantiationException異常,這也是為什么第二個例子中會出現崩潰的原因。
- public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
- try {
- Class<?> clazz = sClassMap.get(fname);
- if (clazz == null) {
- // Class not found in the cache, see if it's real, and try to add it
- clazz = context.getClassLoader().loadClass(fname);
- sClassMap.put(fname, clazz);
- }
- Fragment f = (Fragment)clazz.newInstance();
- if (args != null) {
- args.setClassLoader(f.getClass().getClassLoader());
- f.mArguments = args;
- }
- return f;
- } catch (ClassNotFoundException e) {
- throw new InstantiationException("Unable to instantiate fragment " + fname
- + ": make sure class name exists, is public, and has an"
- + " empty constructor that is public", e);
- } catch (java.lang.InstantiationException e) {
- throw new InstantiationException("Unable to instantiate fragment " + fname
- + ": make sure class name exists, is public, and has an"
- + " empty constructor that is public", e);
- } catch (IllegalAccessException e) {
- throw new InstantiationException("Unable to instantiate fragment " + fname
- + ": make sure class name exists, is public, and has an"
- + " empty constructor that is public", e);
- }
- }
可以看到場景二提示的errormsg跟拋出的異常是可以對應上的,其實Fragment源碼里面也說得很清楚:
- /**
- * Default constructor. <strong>Every</strong> fragment must have an
- * empty constructor, so it can be instantiated when restoring its
- * activity's state. It is strongly recommended that subclasses do not
- * have other constructors with parameters, since these constructors
- * will not be called when the fragment is re-instantiated; instead,
- * arguments can be supplied by the caller with {@link #setArguments}
- * and later retrieved by the Fragment with {@link #getArguments}.
- *
- * <p>Applications should generally not implement a constructor. The
- * first place application code an run where the fragment is ready to
- * be used is in {@link #onAttach(Activity)}, the point where the fragment
- * is actually associated with its activity. Some applications may also
- * want to implement {@link #onInflate} to retrieve attributes from a
- * layout resource, though should take care here because this happens for
- * the fragment is attached to its activity.
- */
- public Fragment() {
- }
大意就是,Fragment必須有一個空構造方法,這樣才能保證重建流程,并且,Fragment的子類也不推薦有帶參數的構造方法,最好采用setArguments來保存參數。下面再來看下為什么會出現兩個DialogFragment。
為什么出現兩個DialogFragment
Fragment在被創建之后,如果不通過add或者replace添加到Activity的布局中是不會顯示的,在保存現場的時候,也是保存了add的這個狀態的,來看一下Fragment的add邏輯:此時被后臺殺死,或旋轉屏幕,被恢復的DialogFragmentActivity時會出現兩個FragmentDialog,一個被系統恢復的,一個新建的。
Add一個Fragment,并顯示的原理--所謂Fragment生命周期
通常我們FragmentActivity使用Fragment的方法如下:假設是在oncreate函數中:
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Fragment fr = Fragment.instance("")
- getSupportFragmentManager().beginTransaction()
- .add(R.id.container,fr).commit();
其中getSupportFragmentManager返回的是FragmentManager的子類FragmentManagerImpl,FragmentManagerImpl是FragmentActivity的一個內部類,其Fragment的管理邏輯都是由FragmentManagerImpl來處理的,本文是基于4.3,后面的高版本引入了FragmentController其實也只是多了一層封裝,原理差別不是太大,有興趣可以自己分析:
- public class FragmentActivity extends Activity{
- ...
- final FragmentManagerImpl mFragments = new FragmentManagerImpl();
- ...
- final FragmentContainer mContainer = new FragmentContainer() {
- @Override
- @Nullable
- public View findViewById(int id) {
- return FragmentActivity.this.findViewById(id);
- }
- @Override
- public boolean hasView() {
- Window window = FragmentActivity.this.getWindow();
- return (window != null && window.peekDecorView() != null);
- }
- };
FragmentManagerImpl的beginTransaction()函數返回的是一個BackStackRecord()
- @Override
- public FragmentTransaction beginTransaction() {
- return new (this);
- }
從名字就可以看出,beginTransaction是為FragmentActivity生成一條Transaction(事務),可以執行,也可以反向,作為退棧的一個依據,FragmentTransaction的add函數實現如下,
- public FragmentTransaction add(Fragment fragment, String tag) {
- doAddOp(0, fragment, tag, OP_ADD);//異步操作的,跟Hander類似
- return this;
- }
- private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
- fragment.mFragmentManager = mManager;
- ...
- Op op = new Op();
- op.cmd = opcmd;
- op.fragment = fragment;
- addOp(op);
- }
之后commit這個Transaction, 將Transaction插入到Transaction隊列中去,最終會回調FragmentManager的addFragment方法,將Fragment添加FragmentManagerImpl到維護Fragment列表中去,并且根據當前的Activity狀態,將Fragment調整到合適的狀態,代碼如下:
- public void addFragment(Fragment fragment, boolean moveToStateNow) {
- if (mAdded == null) {
- mAdded = new ArrayList<Fragment>();
- }
- makeActive(fragment);
- if (!fragment.mDetached) {
- if (mAdded.contains(fragment)) {
- throw new IllegalStateException("Fragment already added: " + fragment);
- }
- mAdded.add(fragment);
- fragment.mAdded = true;
- fragment.mRemoving = false;
- if (fragment.mHasMenu && fragment.mMenuVisible) {
- mNeedMenuInvalidate = true;
- }
- if (moveToStateNow) {
- moveToState(fragment);
- }
- }
- }
為什么說FragmentManager是FragmentActivity管理Fragment的核心呢,請看下面:
- final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
- ...
- ArrayList<Runnable> mPendingActions;
- Runnable[] mTmpActions;
- boolean mExecutingActions;
- ArrayList<Fragment> mActive;
- ArrayList<Fragment> mAdded;
- ArrayList<Integer> mAvailIndices;
- ArrayList<BackStackRecord> mBackStack;
可以看出FragmentManagerImpl幫FragmentActivity維護著所有管理Fragment的列表,FragmentManagerImpl的State是和Activity的State一致的,這是管理Fragment的關鍵。其實Fragment自身是沒有什么生命周期的,它只是一個View的封裝,完全依靠FragmentManagerImpl來進行同步模擬生命周期,比如在onCreate函數中創建Fragment,add后,在執行的到Activity自身的onCreateView之前,Fragment的onCreateView是不會執行的,也就是Fragment是被動式的跟FragmentActivity保持一致。既然Fragment只是個View的封裝,那么它是如何轉換成View,并添加到Container中去的呢?關鍵是moveToState函數,這個函數強制將新add的Fragment的生命周期與Activity同步:
- void moveToState(Fragment f, int newState, int transit, int transitionStyle,
- boolean keepActive) {
- ...
- if (f.mState < newState) { //低于當前Activity的狀態
- switch (f.mState) {
- case Fragment.INITIALIZING:
- ...
- f.mActivity = mActivity;
- f.mParentFragment = mParent;
- f.mFragmentManager = mParent != null
- ? mParent.mChildFragmentManager : mActivity.mFragments;
- f.mCalled = false;
- f.onAttach(mActivity);
- ...
- if (!f.mRetaining) {
- f.performCreate(f.mSavedFragmentState);
- }
- case Fragment.CREATED:
- if (newState > Fragment.CREATED) {
- f.mView = f.performCreateView(f.getLayoutInflater(
- f.mSavedFragmentState), container, f.mSavedFragmentState);
- f.onViewCreated(f.mView, f.mSavedFragmentState);
- f.performActivityCreated(f.mSavedFragmentState);
- if (f.mView != null) {
- f.restoreViewState(f.mSavedFragmentState);
- }
- f.mSavedFragmentState = null;
- }
- case Fragment.ACTIVITY_CREATED:
- case Fragment.STOPPED:
- if (newState > Fragment.STOPPED) {
- f.performStart();
- }
- case Fragment.STARTED:
- if (newState > Fragment.STARTED) {
- f.mResumed = true;
- f.performResume();
可以看出,add Fragment之后,需要讓Fragment跟當前Activity的State保持一致?,F在回歸正題,對于后臺殺死狀態下,為什么會show兩個DialogFragment呢,我們需要接著看就要Fragment的異常處理的流程,在Fragment沒有無參構造方法會引發崩潰里面,分析只是走到了Fragment的構建,現在接著往下走。提供無參構造函數后,Fragment可以正確的新建出來,之后呢?之后就是一些恢復邏輯,接著看restoreAllState
- void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
- if (state == null) return;
- FragmentManagerState fms = (FragmentManagerState)state;
- mActive = new ArrayList<Fragment>(fms.mActive.length);
- for (int i=0; i<fms.mActive.length; i++) {
- FragmentState fs = fms.mActive[i];
- if (fs != null) {
- Fragment f = fs.instantiate(mActivity, mParent);
- mActive.add(f);
- fs.mInstance = null;
- // Build the list of currently added fragments.
- if (fms.mAdded != null) {
- mAdded = new ArrayList<Fragment>(fms.mAdded.length);
- for (int i=0; i<fms.mAdded.length; i++) {
- Fragment f = mActive.get(fms.mAdded[i]);
- if (f == null) {
- throwException(new IllegalStateException(
- "No instantiated fragment for index #" + fms.mAdded[i]));
- }
- f.mAdded = true;
- if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
- if (mAdded.contains(f)) {
- throw new IllegalStateException("Already added!");
- }
- mAdded.add(f);
- }
- // Build the back stack.
- if (fms.mBackStack != null) {
- mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
- for (int i=0; i<fms.mBackStack.length; i++) {
- BackStackRecord bse = fms.mBackStack[i].instantiate(this);
- mBackStack.add(bse);
- if (bse.mIndex >= 0) {
- setBackStackIndex(bse.mIndex, bse);
- }
其實到現在現在Fragment相關的信息已經恢復成功了,之后隨著FragmentActivity周期顯示或者更新了,這些都是被殺死后,在FragmentActiivyt的onCreate函數處理的,也就是默認已經將之前的Fragment添加到mAdded列表中去了,但是,在場景一,我們有手動新建了一個Fragment,并添加進去,所以,mAdded函數中就有連個兩個Fragment。這樣,在FragmentActivity調用onStart函數之后,會新建mAdded列表中Fragment的視圖,將其添加到相應的container中去,并在Activity調用onReusume的時候,顯示出來做的,這個時候,就會顯示兩份,其實如果,在這個時候,你再殺死一次,恢復,就會顯示三分,在殺死,重啟,就是四份。。。。
- @Override
- protected void onStart() {
- super.onStart();
- mStopped = false;
- mReallyStopped = false;
- mHandler.removeMessages(MSG_REALLY_STOPPED);
- if (!mCreated) {
- mCreated = true;
- mFragments.dispatchActivityCreated();
- }
- mFragments.noteStateNotSaved();
- mFragments.execPendingActions();
- mFragments.doLoaderStart();
- // NOTE: HC onStart goes here.
- mFragments.dispatchStart();
- mFragments.reportLoaderStart();
- }
以上就是針對兩個場景,對FramgentActivity的一些分析,主要是回復時候,對于Framgent的一些處理。
onSaveInstanceState與OnRestoreInstance的調用時機
在在點擊home鍵,或者跳轉其他界面的時候,都會回調用onSaveInstanceState,但是再次喚醒卻不一定調用OnRestoreInstance,這是為什么呢?onSaveInstanceState與OnRestoreInstance難道不是配對使用的?在Android中,onSaveInstanceState是為了預防Activity被后臺殺死的情況做的預處理,如果Activity沒有被后臺殺死,那么自然也就不需要進行現場的恢復,也就不會調用OnRestoreInstance,而大多數情況下,Activity不會那么快被殺死。
onSaveInstanceState的調用時機
onSaveInstanceState函數是Android針對可能被后臺殺死的Activity做的一種預防,它的執行時機在2.3之前是在onPause之前,2.3之后,放在了onStop函數之前,也就說Activity失去焦點后,可能會由于內存不足,被回收的情況下,都會去執行onSaveInstanceState。對于startActivity函數的調用很多文章都有介紹,可以簡單參考下老羅的博客Android應用程序內部啟動Activity過程(startActivity)的源代碼分析,比如在Activity A 調用startActivity啟動Activity B的時候,會首先通過AMS pause Activity A,之后喚起B,在B顯示,再stop A,在stop A的時候,需要保存A的現場,因為不可見的Activity都是可能被后臺殺死的,比如,在開發者選項中打開不保留活動,就會達到這種效果,在啟動另一個Activity時,上一個Activity的保存流程大概如下,這里先簡單描述,在下一篇原理篇的時候,會詳細講解下流程:
在2.3之后,onSaveInstanceState的時機都放在了onStop之前,看一下FragmentActivity的onSaveInstanceState源碼:
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- Parcelable p = mFragments.saveAllState();
- if (p != null) {
- outState.putParcelable(FRAGMENTS_TAG, p);
- }
- }
可以看出,首先就是父類的onSaveInstanceState,主要是保存一些窗口及View的信息,比如ViewPager當前顯示的是第幾個View等。之后,就是就是通過FragmentManager的saveAllState,來保存FragmentActivity自身的現場-Fragment的一些狀態,這些數據是FragmentActivity恢復Framgent所必須的數據,處理不好就會出現上面的那種異常。
OnRestoreInstanceState的調用時機
之前已經說過,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做的兼容,ViewPager在后臺殺死的情況下,仍然能恢復到上次關閉的位置,這也是對體驗的一種優化,這其中的原理是什么?之前分析onSaveInstanceState與onRestoreInstanceState的時候,只關注了Fragment的處理,其實還有一些針對Window窗口及Vie的處理,先看一下onSaveInstanceState針對窗口保存了什么:
- protected void onSaveInstanceState(Bundle outState) {
- outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
- }
PhonwWinow.java
- @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<Parcelable> 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
- ublic 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應對后臺殺死做的兼容
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();
- }
- }
App開發時針對后臺殺死處理方式
最簡單的方式,但是效果一般:取消系統恢復
比如:針對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如何處理殺死及恢復的文章。