App在後臺久置後,再次從桌面或最近的任務列表喚醒時常常會發生崩潰,這每每是App在後臺被系統殺死,再次恢復的時候遇到了問題,而在使用FragmentActivity+Fragment的時候會更加頻繁。好比,若是Fragment沒有提供默認構造方法,就會在重建的時候由於反射建立Fragment失敗而崩潰,再好比,在onCreate裏面new 一個FragmentDialog,而且show,被後臺殺死後,再次喚醒的時候,就會show兩個對話框,這是爲何?其實這就涉及了後臺殺死及恢復的機制,其中涉及的知識點主要是FragmentActivity、ActivityManagerService、LowMemoryKiller機制、ActivityStack、Binder等一系列知識點。放在一篇文章裏面可能會有些長,所以,Android後臺殺死系列寫了三篇:html
開篇:FragmentActivity及PhoneWindow後臺殺死處理機制java
原理篇1:後臺殺死與LowmemoryKiller(主要講述App被後臺殺死的原理)android
原理篇2:後臺殺死與App現場恢復(主要講述AMS如何爲App恢復現場的原理)git
本篇是Android後臺殺死系列的第一篇,主要講解在開發過程當中,因爲後臺殺死涉及的一些崩潰,以及如何避免這些崩潰,還有就是簡單的介紹一下onSaveInstanceState與onRestoreInstanceState執行時機與原理,這兩個函數也是Android面試時常問的兩個點,是比簡單的啓動模式Activity聲明週期稍微更深刻細緻一些的地方,也經過這個點引入後臺殺死及恢復原理。github
當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,而且丟棄默認無參構造方法:app
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)在恢復時作了不少咱們沒有看到的事情,先看一下崩潰:ide
看一下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。
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保持一致。如今迴歸正題,對於後臺殺死狀態下,爲何會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的一些處理。
在在點擊home鍵,或者跳轉其餘界面的時候,都會回調用onSaveInstanceState,可是再次喚醒卻不必定調用OnRestoreInstance,這是爲何呢?onSaveInstanceState與OnRestoreInstance難道不是配對使用的?在Android中,onSaveInstanceState是爲了預防Activity被後臺殺死的狀況作的預處理,若是Activity沒有被後臺殺死,那麼天然也就不須要進行現場的恢復,也就不會調用OnRestoreInstance,而大多數狀況下,Activity不會那麼快被殺死。
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雖然與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()); }
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 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如何處理殺死及恢復的文章。