全部評論(1條)
-
- lxxj201000 2016-07-06 00:00:00
- 1、發(fā)生了頁面重啟(旋轉(zhuǎn)屏幕、內(nèi)存不足等情況被強殺重啟)。2、使用add()方式加載Fragment; 為什么會發(fā)生Fragment重疊? 從源碼角度分析,為什么發(fā)生頁面重啟后會導致重疊?(在以add方式加載Fragment的時候) 我們知道Activity中有個onSaveInstanceState()方法,該方法在app進入后臺、屏幕旋轉(zhuǎn)前、跳轉(zhuǎn)下一個 Activity等情況下會被調(diào)用,此時系統(tǒng)幫我們保存一個Bundle類型的數(shù)據(jù),我們可以根據(jù)自己的需求,手動保存一些例如播放進度等數(shù)據(jù),而后如果 發(fā)生了頁面重啟,我們可以在onRestoreInstanceState()或onCreate()里get該數(shù)據(jù),從而恢復播放進度等狀態(tài)。 而產(chǎn)生Fragment重疊的原因就與這個保存狀態(tài)的機制有關(guān),大致原因就是系統(tǒng)在頁面重啟前,幫我們保存了Fragment的狀態(tài),但是在重啟后恢復時,視圖的可見狀態(tài)沒幫我們保存,而Fragment默認的是show狀態(tài),所以產(chǎn)生了Fragment重疊現(xiàn)象。 分析: 我們先看FragmentActivity的相關(guān)源碼: public class FragmentActivity extends ... { final FragmentController mFragments = FragmentController.createController(new HostCallbacks()); protected void onCreate(@Nullable Bundle savedInstanceState) { ...省略 if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, nc != null ? nc.fragments : null); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Parcelable p = mFragments.saveAllState(); ...省略 } } 從上面源碼可以看出,F(xiàn)ragmentActivity確實是幫我們保存了Fragment的狀態(tài),并且在頁面重啟后會幫我們恢復! 其中的mFragments是FragmentController,它是一個Controller,內(nèi)部通過FragmentHostCallback間接控制FragmentManagerImpl。相關(guān)代碼如下: public class FragmentController { private final FragmentHostCallback<?> mHost; public Parcelable saveAllState() { return mHost.mFragmentManager.saveAllState(); } public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) { mHost.mFragmentManager.restoreAllState(state, nonConfigList); } } public abstract class FragmentHostCallback<E> extends FragmentContainer { final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl(); } 通過上面代碼可以看出FragmentController通過FragmentHostCallback里的FragmentManagerImpl對象來控制恢復工作。 我們接著看FragmentManagerImpl到底做了什么: final class FragmentManagerImpl extends FragmentManager { Parcelable saveAllState() { ...省略 詳細保存過程 FragmentManagerState fms = new FragmentManagerState(); fms.mActive = active; fms.mAdded = added; fms.mBackStack = backStack; return fms; } void restoreAllState(Parcelable state, List<Fragment> nonConfig) { // 恢復核心代碼 FragmentManagerState fms = (FragmentManagerState)state; FragmentState fs = fms.mActive[i]; if (fs != null) { Fragment f = fs.instantiate(mHost, mParent); } } } 我們通過saveAllState()看到了關(guān)鍵的保存代碼,原來是是通過FragmentManagerState來保存Fragment的狀態(tài)、所處Fragment棧下標、回退棧狀態(tài)等。 而在restoreAllState()恢復時,通過FragmentManagerState里的FragmentState的instantiate()方法恢復了Fragment(見下面的分析就明白啦) 我們看下FragmentManagerState: final class FragmentManagerState implements Parcelable { FragmentState[] mActive; // Fragment狀態(tài) int[] mAdded; // 所處Fragment棧下標 BackStackState[] mBackStack; // 回退棧狀態(tài) ... } 我們只看FragmentState,它也實現(xiàn)了Parcelable,保存了Fragment的類名、下標、id、Tag、ContainerId以及Arguments等數(shù)據(jù): final class FragmentState implements Parcelable { final String mClassName; final int mIndex; final boolean mFromLayout; final int mFragmentId; final int mContainerId; final String mTag; final boolean mRetainInstance; final boolean mDetached; final Bundle mArguments; ... // 在FragmentManagerImpl的restoreAllState()里被調(diào)用 public Fragment instantiate(FragmentHostCallback host, Fragment parent) { ...省略 mInstance = Fragment.instantiate(context, mClassName, mArguments); } } 至此,我們就明白了系統(tǒng)幫我們保存的Fragment其實Z終是以FragmentState形式存在的。 此時我們再思考下為什么在頁面重啟后會發(fā)生Fragment的重疊? 其實答案已經(jīng)很明顯了,根據(jù)上面的源碼分析,我們會發(fā)現(xiàn)FragmentState里沒有Hidden狀態(tài)的字段! 而Hidden狀態(tài)對應Fragment中的mHidden,該值默認false... public class Fragment ... { boolean mHidden; } 我想你應該明白了,在以add方式加載Fragment的場景下,系統(tǒng)在恢復Fragment時,mHidden=false,即show狀態(tài),這 樣在頁面重啟后,Activity內(nèi)的Fragment都是以show狀態(tài)顯示的,而如果你不進行處理,那么就會發(fā)生Fragment重疊現(xiàn)象! 為什么使用add()加載Fragment會導致重疊? 我們知道加載Fragment有2種方式:replace()和add()。使用replace加載Fragment是不會發(fā)生重疊現(xiàn)象的,只有通過add方式才有可能發(fā)生重疊現(xiàn)象。 我們一般使用add時,會和show(),hide()配合使用,add配合hide是使Fragment的視圖改變?yōu)镚ONE狀態(tài);而 replace是銷毀Fragment 的視圖。頁面重啟時,add的Fragment會全部走生命周期,創(chuàng)建視圖;而replace的非棧頂Fragment不會走生命周期,只有Back時, 才會逐一走棧頂Fragment生命周期,創(chuàng)建視圖。 結(jié)合上面的源碼分析,在使用replace加載Fragment時,頁面重啟后,F(xiàn)ragment視圖都還沒創(chuàng)建,所以mHidden沒有意義,不 會發(fā)生重疊現(xiàn)象;而在使用add加載時,視圖是存在的并且疊加在一起,頁面重啟后 mHidden=false,所有的Fragment都會是show狀態(tài)顯示出來(即VISIBLE),從而造成了Fragment重疊!
-
贊(18)
回復(0)
熱門問答
- 從源碼角度分析,為什么會發(fā)生 Fragment 重疊
2016-07-05 09:20:02
383
1
- 從分子角度分析表面張力
- 從分子角度分析表面張力
2016-09-21 19:14:08
422
1
- 中航油案例, 從內(nèi)控角度分析?
2014-04-28 03:28:04
373
1
- 從馬克思主義哲學角度分析,為什么擠不出牛奶來?
2016-04-07 05:17:34
556
1
- 從生化角度分析為什么不吃早餐對身體不好
2015-01-19 07:29:44
497
2
- 為什么海拔越高,食品包裝袋膨脹起來越多?從物理角度分析。
2010-05-04 03:09:10
649
4
- 從馬克思主義哲學的角度分析,為什么擠不出牛奶來
2017-04-11 06:50:35
509
1
- 從微觀角度分析 臭氧 與氧氣化學性質(zhì)不同
2018-11-23 04:40:15
514
0
- 從生物學的角度分析臭氧層破壞的影響
- 越具體越好
2007-01-13 03:31:43
558
1
- 從切削力與安全角度分析磨床操作者為什么必須站在砂輪側(cè)面
2016-09-28 09:57:34
731
1
- 從流體力學的角度分析自然界中的河流為什么都是彎曲的
2017-07-27 04:47:14
391
1
- 從經(jīng)濟學角度分析,資本主義國家為什么把牛奶倒在海里浪費都不賣
2013-11-03 20:37:20
447
1
- 從營養(yǎng)學角度分析下什么牌子的牛奶好
2017-09-20 22:17:05
325
1
- 從微觀角度分析氯化氫氣體不顯酸性的原因
2014-06-06 10:34:53
566
3
- 為什么光柵會有重疊現(xiàn)象,光柵重疊現(xiàn)象怎樣消除
2015-06-30 17:10:09
687
1
- 單向閥為什么會發(fā)生異常聲音
2017-12-16 12:39:56
593
1
4月突出貢獻榜
推薦主頁
最新話題





參與評論
登錄后參與評論