剖析Fragment的Pause生命周期全过程
前言
之前遇到一個問題,與Fragment的Pause生命周期有關,所以就研究了一下Fragment的Pause生命周期特點。就有關這篇筆記。
我們知道Fragment的生命周期是依賴Activity的,所以想探究Fragment的Pause過程需要從Activity的Pause下手。
Pause過程
在FragmentActivity的onPause可以看到相關代碼,如下:
@Override protected?void?onPause()?{super.onPause();mResumed?=?false;if?(mHandler.hasMessages(MSG_RESUME_PENDING))?{mHandler.removeMessages(MSG_RESUME_PENDING);onResumeFragments();}mFragments.dispatchPause(); }可以看到最后一行代碼啟動了Fragment的Pause過程,mFragments是一個FragmentControler對象,它的dispatchPause方法只有一行代碼
public?void?dispatchPause()?{mHost.mFragmentManager.dispatchPause(); }它調用了FragmentManager的dispatchPause方法
public void dispatchPause() {moveToState(Fragment.STARTED, false); }也只有一行代碼,調用moveToState。在FragmentManager中,這個方法最終會調用另外一個重載的moveToState方法
void?moveToState(int?newState,?boolean?always)?{moveToState(newState,?0,?0,?always); }void?moveToState(int?newState,?int?transit,?int?transitStyle,?boolean?always)?{if?(mHost?==?null?&&?newState?!=?Fragment.INITIALIZING)?{throw?new?IllegalStateException("No?host");}if?(!always?&&?mCurState?==?newState)?{return;}mCurState?=?newState;if?(mActive?!=?null)?{boolean?loadersRunning?=?false;for?(int?i=0;?i<mActive.size();?i++)?{Fragment?f?=?mActive.get(i);if?(f?!=?null)?{moveToState(f,?newState,?transit,?transitStyle,?false);if?(f.mLoaderManager?!=?null)?{loadersRunning?|=?f.mLoaderManager.hasRunningLoaders();}}}if?(!loadersRunning)?{startPendingDeferredFragments();}if?(mNeedMenuInvalidate?&&?mHost?!=?null?&&?mCurState?==?Fragment.RESUMED)?{mHost.onSupportInvalidateOptionsMenu();mNeedMenuInvalidate?=?false;}} }先判斷是否有fragment,如果有則最終調用下面的函數
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive)這個方法很復雜,將近300行,就不將所有源碼都貼出來了。
重點關注這些代碼
} else if (f.mState > newState) {switch (f.mState) {case Fragment.RESUMED:if (newState < Fragment.RESUMED) {if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);f.performPause();}case Fragment.STARTED:if (newState < Fragment.STARTED) {if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);f.performStop();}case Fragment.STOPPED:if (newState < Fragment.STOPPED) {if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f);f.performReallyStop();}case Fragment.ACTIVITY_CREATED:if (newState < Fragment.ACTIVITY_CREATED) {if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f);if (f.mView != null) {// Need to save the current view state if not// done already.if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {saveFragmentViewState(f);}}f.performDestroyView(); //destroy viewif (f.mView != null && f.mContainer != null) {Animation anim = null;if (mCurState > Fragment.INITIALIZING && !mDestroyed) {anim = loadAnimation(f, transit, false,transitionStyle);}if (anim != null) {final Fragment fragment = f;f.mAnimatingAway = f.mView;f.mStateAfterAnimating = newState;final View viewToAnimate = f.mView;anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(viewToAnimate, anim) {@Overridepublic void onAnimationEnd(Animation animation) {super.onAnimationEnd(animation);if (fragment.mAnimatingAway != null) {fragment.mAnimatingAway = null;moveToState(fragment, fragment.mStateAfterAnimating,0, 0, false);}}});f.mView.startAnimation(anim);}f.mContainer.removeView(f.mView); //remove view}f.mContainer = null;f.mView = null;f.mInnerView = null;}case Fragment.CREATED:幾個state如下
static final int INITIALIZING = 0; // Not yet created. static final int CREATED = 1; // Created. static final int ACTIVITY_CREATED = 2; // The activity has finished its creation. static final int STOPPED = 3; // Fully created, not started. static final int STARTED = 4; // Created and started, not resumed. static final int RESUMED = 5; // Created started and resumed.可以看到RESUMED最大
當一個頁面已經展示,那么當前狀態就是RESUMED,Puase時可以在dispathPause方法中看到newState是STARTED,那么上面代碼的第一行判斷就成立,進入這層邏輯中。
由于mState是RESUMED,會依次執行每一個case(因為switch中每個case都沒有break,所以會順序執行下去)。但是當執行到第一個case時,調用了Fragment的performPause方法,
在Fragment源碼中可以看到performPause這個方法
void performPause() {if (mChildFragmentManager != null) {mChildFragmentManager.dispatchPause();}mState = STARTED;mCalled = false;onPause();if (!mCalled) {throw new SuperNotCalledException("Fragment " + this+ " did not call through to super.onPause()");} }如果沒有childFragment,那么直接調用onPause。
如果有childFragment,會先調用它的fragmentManager的dispatchPause方法,這樣就進入了childFragment的Pause過程。這個過程與上面的一致,其實就是一個遞歸的過程。
總結起來,調用順序如圖
以上就是Fragment的Pause整個過程。是從Activity的onPause開始的,而Fragment的onPause是最后被調用的。
問題出現
回到最初,我們遇到的問題到底是什么呢?
首先注意在最后的moveToState函數中有如下代碼
f.performDestroyView(); //destroy view if (f.mView != null && f.mContainer != null) {...f.mContainer.removeView(f.mView); //remove view } f.mContainer = null; f.mView = null; f.mInnerView = null;表示當執行Pause時fragment會destory頁面上的view。
這里就會存在一個問題!!!在Activity中切換Fragment時,在Fragment的Pause周期完全結束之前view就已經被remove了, 而這時Fragment還顯示在屏幕上。如果activity的theme是transparent,且Fragment里有SurfaceView或者MapView這類view,在remove時就會發生透視現象(遮蓋在下面的Activity的內容會顯示出來)。而且為裝載Fragment的容器設置背景色沒有任何用處。
注意這個現象雖然發生在Pause階段,但是由于返回桌面這個操作會瞬間完成,所以這時沒有問題。主要發生在Fragment切換的時候,即getFragmentManager().beginTransaction().replace(...)
如果只是TextView這類普通的就不會存在上面的現象。經過反復測試發現,remove時(在第二個劃線的代碼處)如果TextView這類的view雖然remove了但是還會留存在頁面上,并且會參與轉場動畫(如果有轉場動畫);而SurfaceView這類,remove的時候那塊區域內容會立刻消失!這樣就導致了一瞬間的透視現象。
剖析解決
這是由于SurfaceView的特殊性,實際上remove時SurfaceView與其他類型的View一樣留存,但是它的繪制內容被全部回收了(包括SurfaceView的默認背景 - 黑色),沒有內容則呈現出了一種透明現象。
解決SurfaceView透明的方法,可以為其設置一下format,如
view.surfaceview.setZOrderOnTop(true) view.surfaceview.holder.setFormat(PixelFormat.RGBA_8888)而MapView(百度)沒有找到相關方法,但是有一個取巧的辦法,在切換前,即getFragmentManager().beginTransaction().replace(...)之前前將MapView或其容器設為INVISIBLE,這樣由于沒有MapView的影響,背景色就可以正常顯示了,不會出現透視現象。注意恢復時要記得重新設為VISIBLE。
另外一個徹底解決透視方法是將activity的theme設置為不透明,但是要保證不影響這個activity其他的顯示效果。
深層原因
至于SurfaceView為何沒有內容呈現透明狀態,則容器背景也無效,這個與SurfaceView的繪制有關,引用網上的一段解釋:
用來描述SurfaceView的Layer或者LayerBuffer的Z軸位置是小于用來其宿主Activity窗口的Layer的Z軸位置的,但是前者會在后者的上面挖一個“洞”出來,以便它的UI可以對用戶可見。實際上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不過是在其宿主Activity窗口上設置了一塊透明區域。
所以說當SurfaceView內容完全消失后,這個“洞”就露出了下面頁面的內容,這樣就導致了問題。
????????
?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的剖析Fragment的Pause生命周期全过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为RecyclerView添加下拉刷新(
- 下一篇: android studio中创建、切换