剖析Fragment的Pause生命周期全过程
前言
之前遇到一個(gè)問(wèn)題,與Fragment的Pause生命周期有關(guān),所以就研究了一下Fragment的Pause生命周期特點(diǎn)。就有關(guān)這篇筆記。
我們知道Fragment的生命周期是依賴Activity的,所以想探究Fragment的Pause過(guò)程需要從Activity的Pause下手。
Pause過(guò)程
在FragmentActivity的onPause可以看到相關(guān)代碼,如下:
@Override protected?void?onPause()?{super.onPause();mResumed?=?false;if?(mHandler.hasMessages(MSG_RESUME_PENDING))?{mHandler.removeMessages(MSG_RESUME_PENDING);onResumeFragments();}mFragments.dispatchPause(); }可以看到最后一行代碼啟動(dòng)了Fragment的Pause過(guò)程,mFragments是一個(gè)FragmentControler對(duì)象,它的dispatchPause方法只有一行代碼
public?void?dispatchPause()?{mHost.mFragmentManager.dispatchPause(); }它調(diào)用了FragmentManager的dispatchPause方法
public void dispatchPause() {moveToState(Fragment.STARTED, false); }也只有一行代碼,調(diào)用moveToState。在FragmentManager中,這個(gè)方法最終會(huì)調(diào)用另外一個(gè)重載的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,如果有則最終調(diào)用下面的函數(shù)
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive)這個(gè)方法很復(fù)雜,將近300行,就不將所有源碼都貼出來(lái)了。
重點(diǎn)關(guān)注這些代碼
} 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:幾個(gè)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最大
當(dāng)一個(gè)頁(yè)面已經(jīng)展示,那么當(dāng)前狀態(tài)就是RESUMED,Puase時(shí)可以在dispathPause方法中看到newState是STARTED,那么上面代碼的第一行判斷就成立,進(jìn)入這層邏輯中。
由于mState是RESUMED,會(huì)依次執(zhí)行每一個(gè)case(因?yàn)閟witch中每個(gè)case都沒(méi)有break,所以會(huì)順序執(zhí)行下去)。但是當(dāng)執(zhí)行到第一個(gè)case時(shí),調(diào)用了Fragment的performPause方法,
在Fragment源碼中可以看到performPause這個(gè)方法
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()");} }如果沒(méi)有childFragment,那么直接調(diào)用onPause。
如果有childFragment,會(huì)先調(diào)用它的fragmentManager的dispatchPause方法,這樣就進(jìn)入了childFragment的Pause過(guò)程。這個(gè)過(guò)程與上面的一致,其實(shí)就是一個(gè)遞歸的過(guò)程。
總結(jié)起來(lái),調(diào)用順序如圖
以上就是Fragment的Pause整個(gè)過(guò)程。是從Activity的onPause開(kāi)始的,而Fragment的onPause是最后被調(diào)用的。
問(wèn)題出現(xiàn)
回到最初,我們遇到的問(wèn)題到底是什么呢?
首先注意在最后的moveToState函數(shù)中有如下代碼
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;表示當(dāng)執(zhí)行Pause時(shí)fragment會(huì)destory頁(yè)面上的view。
這里就會(huì)存在一個(gè)問(wèn)題!!!在Activity中切換Fragment時(shí),在Fragment的Pause周期完全結(jié)束之前view就已經(jīng)被remove了, 而這時(shí)Fragment還顯示在屏幕上。如果activity的theme是transparent,且Fragment里有SurfaceView或者M(jìn)apView這類view,在remove時(shí)就會(huì)發(fā)生透視現(xiàn)象(遮蓋在下面的Activity的內(nèi)容會(huì)顯示出來(lái))。而且為裝載Fragment的容器設(shè)置背景色沒(méi)有任何用處。
注意這個(gè)現(xiàn)象雖然發(fā)生在Pause階段,但是由于返回桌面這個(gè)操作會(huì)瞬間完成,所以這時(shí)沒(méi)有問(wèn)題。主要發(fā)生在Fragment切換的時(shí)候,即getFragmentManager().beginTransaction().replace(...)
如果只是TextView這類普通的就不會(huì)存在上面的現(xiàn)象。經(jīng)過(guò)反復(fù)測(cè)試發(fā)現(xiàn),remove時(shí)(在第二個(gè)劃線的代碼處)如果TextView這類的view雖然remove了但是還會(huì)留存在頁(yè)面上,并且會(huì)參與轉(zhuǎn)場(chǎng)動(dòng)畫(如果有轉(zhuǎn)場(chǎng)動(dòng)畫);而SurfaceView這類,remove的時(shí)候那塊區(qū)域內(nèi)容會(huì)立刻消失!這樣就導(dǎo)致了一瞬間的透視現(xiàn)象。
剖析解決
這是由于SurfaceView的特殊性,實(shí)際上remove時(shí)SurfaceView與其他類型的View一樣留存,但是它的繪制內(nèi)容被全部回收了(包括SurfaceView的默認(rèn)背景 - 黑色),沒(méi)有內(nèi)容則呈現(xiàn)出了一種透明現(xiàn)象。
解決SurfaceView透明的方法,可以為其設(shè)置一下format,如
view.surfaceview.setZOrderOnTop(true) view.surfaceview.holder.setFormat(PixelFormat.RGBA_8888)而MapView(百度)沒(méi)有找到相關(guān)方法,但是有一個(gè)取巧的辦法,在切換前,即getFragmentManager().beginTransaction().replace(...)之前前將MapView或其容器設(shè)為INVISIBLE,這樣由于沒(méi)有MapView的影響,背景色就可以正常顯示了,不會(huì)出現(xiàn)透視現(xiàn)象。注意恢復(fù)時(shí)要記得重新設(shè)為VISIBLE。
另外一個(gè)徹底解決透視方法是將activity的theme設(shè)置為不透明,但是要保證不影響這個(gè)activity其他的顯示效果。
深層原因
至于SurfaceView為何沒(méi)有內(nèi)容呈現(xiàn)透明狀態(tài),則容器背景也無(wú)效,這個(gè)與SurfaceView的繪制有關(guān),引用網(wǎng)上的一段解釋:
用來(lái)描述SurfaceView的Layer或者LayerBuffer的Z軸位置是小于用來(lái)其宿主Activity窗口的Layer的Z軸位置的,但是前者會(huì)在后者的上面挖一個(gè)“洞”出來(lái),以便它的UI可以對(duì)用戶可見(jiàn)。實(shí)際上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不過(guò)是在其宿主Activity窗口上設(shè)置了一塊透明區(qū)域。
所以說(shuō)當(dāng)SurfaceView內(nèi)容完全消失后,這個(gè)“洞”就露出了下面頁(yè)面的內(nèi)容,這樣就導(dǎo)致了問(wèn)題。
????????
?
超強(qiáng)干貨來(lái)襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的剖析Fragment的Pause生命周期全过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 为RecyclerView添加下拉刷新(
- 下一篇: 利用Android Studio的 Mo