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); }