你真的了解Fragment的生命周期嗎?


Android Framwork開發人員中的傳奇人物Dianne Hackborn在2010年將Fragment引入了Android,也就是在android3.0之后引入Fragment,他在提交信息中寫道:

 

 

“將單一的Activity拆分成多個獨立的部件”的想法非常好。 然而,從今天Fragment的的實際使用效果來看,這一API的實現和演變並不理想。

 

雖然在項目中我們經常使用Fragment,但Fragment也有局限性和缺點,讓我們來看下面這個流程圖:

 

 

幸運的是,您在應用中使用Fragment的時候無需了解整個生命周期的轉換過程,本文將介紹一些處理Fragment生命周期的方法,它隱藏了大部分的復雜細節。

 

Make Take  On Fragment Lifecycle

處理Fragment生命周期的方法旨在實現兩個目標:

 

1、使Fragment的生命周期處理邏輯盡可能類似於Activity的處理邏輯。

2、使Fragment的生命周期處理邏輯獨立於Activity的生命周期。

 

使用類似於處理Activity的生命周期的方法來處理Fragment的生命周期,能大幅降低應用程序的整體復雜性;這意味着開發過程中的工作量會減少,維護會更容易,新團隊成員熟悉項目的速度會更快,某種程度上,還可以減少bug產生的風險。

 

OnCreat(Bundle)

當Activity被強制銷毀,之后又被自動恢復的時候,Android系統會在這一過程中銷毀並重新創建Fragment。 重新創建的機制是通過使用反射的方法來調用Fragment的無參構造函數來實現的。 因此,如果您是使用帶參數的構造函數來實例化Fragment,並在其中將依賴的對象傳遞給它,那么在保存和恢復后,所有這些依賴的對象都將被設置為null。

 

就像Activity一樣,需要使用onCreate(Bundle)方法作為構造函數的替代者。 Fragment中依賴對象的注入和初始化就發生在這里;

 

與Activity的onCreate(Bundle)方法不同的是, 不得在Fragment的onCreate(Bundle)方法中執行任何與Android View相關的操作, onCreate(Bundle)方法在Fragment被Attach到Activity后僅被調用一次,它無法支持Fragment的View hierarchy的動態化;

 

將Activity轉換為listener也發生在onCreate(Bundle)中,這將比在onAttach(Context)中拋出一個通用的ClassCastException要有意義的多;

 

 

 

 

View  onCreatView(LayoutInflater,ViewGroup,Bundle)

這個方法是Fragment獨有的,這是它與Activity生命周期最顯着的區別。

Activity在生命周期的轉換過程中都只有同一個View hierarchy。 你在Activity的onCreate(Bundle)中初始化這個View hierarchy,然后它就會一直存在於Activity的整個生命周期,直到Activity被垃圾收集器回收為止;

 

然而,在Fragment在其生命周期中可以存在有多個View hierarchy,由Android系統決定何時進行替換;

 

每次需要創建新的View hierarchy的時候,Android系統都會調用onCreateView(LayoutInflater, ViewGroup, Bundle)方法;

 

重寫這個方法的主要原則是:Fragment中所有持有與View hierarchy相關的對象的引用的成員變量,必須在View onCreateView(LayoutInflater,ViewGroup,Bundle)中進行初始化,如果Fragment的成員變量持有View或者相關對象的引用,須確保在此方法中初始化這些成員變量;

 

方法的基本的處理邏輯如下:

 

 

Fragment里每個與View hierarchy相關的成員變量都必須在此方法中初始化, 這包括列表適配器,用戶交互事件的監聽器等。保持Fragment里的代碼可維護性的唯一方法是確保此方法在重新創建整個View hierarchy的時候會重新初始化Fragment里與之相關的成員變量,這也是為了減小內存泄漏的風險。

 

onStart()

Fragment的這個方法與Activity的onStart()方法具有完全相同的職責和指導原則,方法的基本的處理邏輯如下:

 

@Override
public void onStart() {
    super.onStart();
 
    mSomeView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            handleOnSomeViewClick();
        }
    });
 
    mFirstDependency.registerListener(this);
 
    switch (mSecondDependency.getState()) {
        case SecondDependency.State.STATE_1:
            updateUiAccordingToState1();
            break;
        case SecondDependency.State.STATE_2:
            updateUiAccordingToState2();
            break;
        case SecondDependency.State.STATE_3:
            updateUiAccordingToState3();
            break;
    }
 
    if (mWelcomeDialogHasAlreadyBeenShown) {
        mFirstDependency.intiateAsyncFunctionalFlowAndNotify();
    } else {
        showWelcomeDialog();
        mWelcomeDialogHasAlreadyBeenShown = true;
    }
}

 

這個方法里包含了Fragment的大部分功能邏輯。 保持onStart()方法在Activity和Fragment里的一致性具有很多好處。

 

onResume()

這個方法的處理邏輯與Activity的onResume()方法相同。

 

onPause()

這個方法的處理邏輯與Activity的onResume()方法相同。

 

onStop()

這個方法的處理邏輯同樣與Activity的onStop()方法相同,在此方法中,我們可以注銷點擊事件的監聽器,不是必須的,但是也可以減少bug的風險。

 

onDestroyView()

之前說必須在onCreateView(LayoutInflater,ViewGroup,Bundle)里初始化Fragment里所有持有View或者相關對象的引用的成員變量。這個要求源自於這樣一個事實:Fragment的View hierarchy可以被重新創建,所以所有未在該方法中被初始化的持有View的引用對象都將被置為null;

 

如果這樣做了,Fragment將一直持有對這些View的強引用,直到下一次調用View onCreateView(LayoutInflater,ViewGroup,Bundle)方法或者整個Fragment被銷毀;

 

有很多建議是應該在onDestroyView()方法里將所有前面提到的成員變量設置為Null。 目的是盡快釋放這些引用以允許垃圾收集器在onDestroyView()返回后立即回收它們,這樣就能更快地釋放與這些View相關的內存空間,但這是過早優化的經典案例,在絕大多數情況下,你並不需要這種優化,所有,我們不需要重寫onDestroyView()方法。

 

onDestroy()

像Activity一樣,您不需要在Fragment中重寫此方法。

 

onSaveInstanceState(Bundle)

這個方法的基本的處理邏輯如下:

 

這個方法對應於Activity的onSaveInstanceState(Bundle)方法,所以對Activity的onSaveInstanceState(Bundle)的大多數討論也適用於此。但請注意:此方法可能在onDestroy()之前的任何時候被調用;在許多情況下,Fragment可能會被銷毀。

 

根據官方文件:這個方法可能在onDestroy()之前的任何時候調用。 這里有兩個主要問題:

1、與Fragment相關聯的View hierarchy可以被銷毀而不實際保存其狀態。 因此,如果您想在View onCreateView(LayoutInflater,ViewGroup,Bundle)中恢復Fragment的狀態,這會加大程序崩潰的幾率;

 

2、如果在onDestroy()之前的任何時候都可以調用onSaveInstanceState(Bundle),那么就無法保證何時才能安全地更改Fragment的狀態(例如替換嵌套的Fragment)。

 

setRetainInstance(boolean)

Fragment具有屬性retainInstance,默認值為false。 
當設備旋轉時,fragment會隨托管activity一起銷毀並重建。

調用setRetainInstance(true)方法可保留fragment,代碼如下:

 

已保留的fragment不會隨着activity一起被銷毀; 
相反,它會一直保留(進程不消亡的前提下),並在需要時原封不動地傳遞給新的Activity。

 

但是此方法建議不使用,因為此方法改變了Fragment的生命周期。

 

為什么Fragment如此復雜?

也許引入這種機制是為了優化內存的消耗。當Fragment不可見的時候,銷毀Fragment的View hierarchy允許釋放一些內存。 但另一個問題是:Fragment實現了對狀態保存和恢復流程的支持;

 

在FragmentStatePagerAdapter並不是采用這種機制去保存和恢復Fragments的狀態。

借用原作者的話:整個機制可能是一種過早的優化,它沒有任何用處,反而會使得Fragment使用更復雜。

 

 

 結語 

雖然Fragment,整個機制復雜,但是與“一個界面一個Activity”的方法相比,Fragment可以提供更好的用戶體驗;

 

但是我們在使用Fragment的時候,建議在處理Fragment的生命周期時讓它類似於Activity的生命周期並且獨立於它,這使得Fragment的復雜性是可以管理的。

 

小伙伴們,你的支持是我前進的動力,動動手指,掃面二維碼關注我們吧,如果您有任何補充或者想要指出本文中的任何缺陷,請給小編留言哦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM