Fragment的生命週期很是複雜,分爲如下幾種狀況:java
<fragment/>
標籤實例化的,那麼第一個收到的回調將是onInflate
setRetainInstance(true)
,那麼當Activity重建時,Fragment的onDestroy
以及Activity重建後Fragment的onCreate
回調不會被調用.(不管是否將其添加到了返回棧)FragmentTransaction.replace()
,那麼Fragment A會執行onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()
,若是執行FragmentTransaction.replace().addToBackStack()
,那麼Fragment A會執行onPause()->onStop()->onDestroyView()
onHiddenChanged()
onPause()->onStop()->onDestroyView()
,注意:onDestroy()和onDetach()不會調用executePendingTransactions()
executePendingTransactions()
方法來當即執行,可是爲何默認是異步的呢??(我以爲是由於:提交一個Transaction,會致使Fragment的生命週期方法的執行,甚至是多個回調的執行,若是Fragment在這些回調中又提交新的Transaction,那麼可能會破壞當前Transaction的狀態,比方說這是一個pop操做)在使用Fragment的過程當中,經常會遇到在Activity的onSaveInstanceState方法調用以後,操做commit或者popBackStack而致使的crash.
由於在onSaveInstanceState方法以後的操做狀態可能會丟失,所以Android framework默認會拋出一個異常.
對於commit方法來講,單純避免這個異常很簡單,使用commitAllowingStateLoss方法便可.可是popBackStack以及popBackStackImmediate也都會檢查state(checkStateLoss),特別須要注意的是Activity的onBackPressed方法android
public void onBackPressed() { if (!mFragments.popBackStackImmediate()) {//注意 supportFinishAfterTransition(); } }
若是onBackPressed在onSavedInstanceState以後調用,那麼就會crash.
onBackPressed的調用時機:異步
* targetSdkVersion <= 5,在onKeyDown中調用 * targetSdkVersion > 5,在onKeyUp中調用
onSavedInstanceState的調用時機(若是調用的話):ide
* 必定在onStop以前 * 可能在onPause以前,也可能在onPause與onStop之間
須要注意的是: onSavedInstanceState方法不必定會調用,只有在Activity由於某些緣由而被Framework銷燬,而且以後還須要從新建立的狀況,才須要調用(例如:旋屏,或者內存不足而回收返回棧中的某些Activity)oop
舉例: * Activity A在前臺時,屏幕逐漸變暗直至鎖屏,那麼A的onSavedInstanceState會被調用 * Activity A start Activity B,Activity A的onSavedInstanceState會被調用 * Activity A由於返回鍵或者finish調用而返回到上一個界面,那麼A的onSavedInstanceState不會被調用
所以,當onBackPressed在onSavedInstanceState方法以後調用,就必定會crash.解決方法主要有兩種:post
重寫Activity的onSavedInstanceState()方法,而且註釋掉super調用.
這種方法能避免crash,可是它會致使整個Activity的狀態丟失.以DialogFragment爲例,正常狀況下,顯示的DialogFragment在旋屏Activity從新建立以後,不須要咱們處理,Dialog會自動顯示出來(參見DialogFragment.onStart()),可是註釋掉Activity的onSavedInstanceState()方法以後,Fragment狀態丟失,Activity從新建立以後,Dialog也就不會再顯示出來了.ui
更好且通用的作法:在調用commit,popBackStack以及onBackPressed方法以前,判斷onSavedInstanceState()方法是否已經執行,而且onResume方法尚未執行,若是不是,那麼直接操做,不然加入到pending隊列,等待onResumeFragments或者onPostResume以後再執行.this
注意:不要在onResume中操做,由於這時候FragmentManager中的mStateSaved依然多是true.(若是執行順序是onSavedInstanceState()->onPause()->onResume() 或者 onPause()->onSavedInstanceState()->onResume())spa
例如: public void onDataReceived() { if(isStateSaved()) {//isStateSaved()由BaseActivity提供 addPendingFragmentOperation(new Runnable() { @Override public void run() { getSupportFragmentManager().popBackStackImmediate(); } }); } else { getSupportFragmentManager().popBackStackImmediate(); } } @Override protected void onPostResume() { super.onPostResume(); if(pendingFragmentOperation != null && !pendingFragmentOperation.isEmpty()) { for(Runnable operation : pendingFragmentOperation) { operation.run(); } pendingFragmentOperation.clear(); } }
requestCode
的可用區間:調試
建議: requestCode
的取值統一限制在[-1, 65535]之間
首先要說的是儘可能不要使用嵌套Fragment.
當在嵌套Fragment中使用startActivityForResult()時,會遇到的問題:
總之那個發起startActivityForResult()的嵌套Fragment是必定不會收到onActivityResult()回調的.
緣由以下:(可參考上面說的requestCode)
FragmentActivity.startActivityFromFragment()會改動requestCode,用高16比特存儲Fragment在FragmentManager中的index,而低16比特做爲Fragment可用的requestCode.在FragmentActivity.onActivityResult()中,根據高16比特,從FragmentManager中找到對應的Fragment,而後將低16比特的值做爲requestCode,調用Fragment.onActivityResult().
那麼requestCode中只能存儲一個index,即root FragmentManager中的Fragment index.所以就會出現上面所列出的情形:
解決方案:
不使用嵌套Fragment :)
依然利用requestCode,將其低16位拆分,其中的高8位用來存儲childFragmentManager中的index,低8位留給ChildFragment使用.(若是嵌套層級不深,那麼此方案仍是不錯的,若是層級較深,那麼留給Fragment的requestCode的可用值區間將很是侷限)
Android 4.2(Api 17)之後,可使用內置的Fragment,以及ChildFragmentManager,內置Fragment再也不須要藉助requestCode的高16比特來記錄它的index.而是由Framework收到Fragment.startActivityForResult()時,記錄該Fragment的標識(android:fragment:${parentIndex}:${myIndex}
),派發result時,就根據這個標識找到那個Fragment.所以就不會出現ChildFragment收不到onActivityResult()回調的問題了.能夠參考Activity.dispatchActivityResult()
FragmentManager.enableDebugLogging(BuildConfig.DEBUG);
protected void onPostResume() { super.onPostResume(); mHandler.removeMessages(MSG_RESUME_PENDING); onResumeFragments(); mFragments.execPendingActions(); } protected void onResumeFragments() { mFragments.dispatchResume(); }
若是須要在Fragment的onResume都執行完後再執行某個操做,能夠重寫onPostResume()方法,必定要調用 super.onPostResume()
mHost==null
,所以在執行這些操做以前,須要先判斷一下isAdded()
.注意: 這裏不要使用isDetached()
來判斷,由於Fragment被detach以後,它的isDetached()
方法依然可能返回false
isDetached()
將返回false
isDetached()
將返回true
final public Resources getResources() { if (mHost == null) { throw new IllegalStateException("Fragment " + this + " not attached to Activity"); } return mHost.getContext().getResources(); } public void startActivity(Intent intent, @Nullable Bundle options) { if (mHost == null) { throw new IllegalStateException("Fragment " + this + " not attached to Activity"); } mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options); }