Fragment,一個因愛生恨的組件。兼容大屏,適配多尺寸,持久化狀態,作為加載器,Fragment都行。既然如此通用,那就用起來。隨著項目UI越演復雜,功能需求日漸增多,突然發現出現了很多無可理喻的bug,而且都是跟Fragment密切相關的。何以解憂嗎,唯有源碼。
源碼版本是android support 23.4.0,Fragment用的是v4包下。
1.0 第一刀 FragmentManager
Fragment的使用分為兩種情況
- 依附于宿主FragmentActivity
- 嵌套Fragment
以下情況比較常見,作為例子最好不過。
圖1.簡單嵌套
宿主Activity(只針對FragmentActivity及其子類)嵌套一個父Fragment,在父Fragment下又嵌套三個同級的子Fragment。
思考 如何取到各自的FragmentManager? 和 什么時候需要用哪層的FragmentManager? 。
1.1 Activity FragmentManager
Activity獲取FragmentManager的方法有
getSupportFragmentManager();
跟蹤源碼后發現指向FragmentHostCallback
final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl(); FragmentManagerImpl getFragmentManagerImpl() { return mFragmentManager; }
橫向箭頭表示 持有關系 。
圖2.鏈式持有
1.2 Fragment FragmentManager
Fragment獲取FragmentManager的方法
getFragmentManager(); getChildFragmentManager();
跟蹤第一個方法源碼Fragment.java
FragmentManagerImpl mFragmentManager; final public FragmentManager getFragmentManager() { return mFragmentManager; } public Fragment instantiate(FragmentHostCallback host, Fragment parent) { //省略代碼 mInstance.mFragmentManager = host.mFragmentManager; }
原來還是FragmentHostCallback.mFragmentManager,與宿主Activity相同,得出以下結論:
Activity.getSupportFragmentManager()與Fragment.getFragmentManager相同。
跟蹤第二個方法源碼Fragment.java
// Private fragment manager for child fragments inside of this one. FragmentManagerImpl mChildFragmentManager; void instantiateChildFragmentManager() { mChildFragmentManager = new FragmentManagerImpl(); mChildFragmentManager.attachController(mHost, new FragmentContainer() { @Override @Nullable public View onFindViewById(int id) { if (mView == null) { throw new IllegalStateException(quot;Fragment does not have a viewquot;); } return mView.findViewById(id); } @Override public boolean onHasView() { return (mView != null); } }, this); }
先不管FragmentManager如何初始化,可以看出ChildFragmentManager是Fragment私有的。
回到圖1.簡單嵌套中的實際情況。
A.getSupportFragmentManager()與B.getFragmentManager()相同,取到Activity的FragmentManager。
B.getChildFragmentManager與C/D/E.getParentFragment().getChildFramgnetManager()相同,取到FramgmentB的FragmentManager。
C/D/E.getChildFramgnetManager()是沒多大意義的。
2.0 第二刀 Fragment的生命周期
如圖2鏈式持有的類圖我們可以簡單了解這幾個類的關系。
再看一張Fragment創建的時序圖
圖3.Fragment創建的時序圖
創建簡單分為兩步:
- onCreate,圖中1~7,Fragment.mState置為Fragment.INITIALIZING。
- onCreateView,圖中8~15,在10.onCreateView()會將狀態置為Fragment.CREATED。
2.1 創建過程的要點
在FragmentActivity創建時就調用
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
給FragmentController.mHost賦值,而不會置空。
反觀FragmentManager是在
public void attachController(FragmentHostCallback host, FragmentContainer container, Fragment parent) { if (mHost != null) throw new IllegalStateException(quot;Already attachedquot;); mHost = host; mContainer = container; mParent = parent; }
中賦值,attachController會在2.attachHost()中調用,而在dispatchDestroy()中 置空 。mHost 為null時容易導致
if (mHost == null amp;amp; newState != Fragment.INITIALIZING) { throw new IllegalStateException(quot;No hostquot;);}
回到重點。方法moveToState()會陸續調用Fragment的生命周期方法。一直不停初始化到Fragment.performResume()方法,此時Fragment.mState置為Fragment.RESUMED(此過程略過,感興趣請自行閱讀源碼)。
注:moveToState快速理解:
f.mState lt; newState:是fragment創建;f.mState gt; newState:是fragment銷毀;而且,switch并沒有break,需要當心。
貼出官方生命周期圖:
圖4.fragment_lifecycle.png
2.2 Fragment的銷毀
隨著FragmentActivity調用
/** * Dispatch onPause() to fragments. */ @Override protected void onPause() { super.onPause(); mResumed = false; if (mHandler.hasMessages(MSG_RESUME_PENDING)) { mHandler.removeMessages(MSG_RESUME_PENDING); onResumeFragments(); } mFragments.dispatchPause(); }
經過moveToState()執行了fragment.performPause(),此時Fragment.mState置為Fragment.STARTED。
接著FragmentActivity調用
/** * Dispatch onStop() to all fragments. Ensure all loaders are stopped. */ @Override protected void onStop() { super.onStop(); mStopped = true; mHandler.sendEmptyMessage(MSG_REALLY_STOPPED); mFragments.dispatchStop(); }
經過moveToState()執行了fragment.performStop(),此時Fragment.mState置為Fragment.STOPPED。
看一下時序圖
圖5.Fragment.onStop時序圖
此時Fragment.mState置為Fragment.ACTIVITY_CREATED。
最后FragmentActivity調用
/** * Destroy all fragments and loaders. */ @Override protected void onDestroy() { super.onDestroy(); doReallyStop(false); mFragments.dispatchDestroy(); mFragments.doLoaderDestroy(); }
又重新調用了doReallyStop(false)確保已經走完stop的生命周期,到moveToState后接著走framgnet.performDestroy()和fragment.onDetach()。此時Fragment.mState置為Fragment.INITIALIZING。
生命周期小結:
- 從源碼來看,Fragment的生命周期完全依賴與FragmentActivity,而且并 不是 相當嚴謹,這也是為什么嵌套Fragment如此容易引發各種版本兼容問題。
- 未提到動畫的加載,其實會導致很多問題。
3.0 第三刀 Fragment持久化
這句代碼應該寫過無數遍了
setRetainInstance(true);
官方文檔是
控制Activity重建時(如屏幕旋轉)是否會持久化Fragment,只能用在不寸在后臺堆棧中的Framgnet中,如果設置true,生命周期會有變化:不會調用onDestory(),不會調用onCreate(Bundle)...
源碼如下
public void setRetainInstance(boolean retain) { if (retain amp;amp; mParentFragment != null) { throw new IllegalStateException( quot;Can't retain fragements that are nested in other fragmentsquot;); } mRetainInstance = retain; }
不能放在嵌套Fragment中。
看一眼FragmentActivity,在持久化狀態時調用
@Override public final Object onRetainNonConfigurationInstance() { //... Listlt;Fragmentgt; fragments = mFragments.retainNonConfig(); NonConfigurationInstances nci = new NonConfigurationInstances(); nci.fragments = fragments; //... return nci; }
跟蹤到FragmentManager
ArrayListlt;Fragmentgt; retainNonConfig() { ArrayListlt;Fragmentgt; fragments = null; if (mActive != null) { for (int i=0; ilt;mActive.size(); i ) { Fragment f = mActive.get(i); if (f != null amp;amp; f.mRetainInstance) { if (fragments == null) { fragments = new ArrayListlt;Fragmentgt;(); } fragments.add(f); f.mRetaining = true; f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; if (DEBUG) Log.v(TAG, quot;retainNonConfig: keeping retained quot; f); } } } return fragments; }
從mActive的列表中選出setRetainInstance(true)的Fragment。
再回來看看FragmentActivity.onCreate()
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { //... NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); mFragments.restoreAllState(p, nc != null ? nc.fragments : null); //... }
FragmentActivity創建時會重新給這些Fragment賦值。
小結:setRetainInstance(true)略過了Fragment的onCreate和onDestory生命周期,等于由FragmentActivity控制Fragment的
持久化和恢復。
4.0 學到的
- 委托模式
FragmentActivity將生命周期與Fragment相關的方法委托給FragmentController,剝離了耦合關系。 - 代理模式
FragmentController的構造方法中使用FragmentHostCallback作為抽象,而FragmentActivity.HostCallbacks作為真正實現者。 - 避免使用復雜的生命周期方法
盡量不要嵌套Fragment...不要相信...依賴...避免...Fragment的生命周期,能不用就不用吧,可以使用GitHub上的更簡介的三方庫,但是項目中依賴太深的也只能自己踩坑自己填。
5.0 未出鞘之四五六七
Transction,動畫,LoaderManager,版本兼容...各種坑,有時間再填。
6.0 小結
Fragment的創建,銷毀,持久化,是三把利刃,可以很好地解決問題,萬一出現問題,那就要當心刀刃的方向是不是自己。
Tags: 安卓開發
文章來源:http://www.jianshu.com/p/e15c74f86303