Fragment生命周期

每個 Fragment 執行個體都有專屬的生命週期。當使用者瀏覽並與應用程式互動時,片段會在新增、移除和進入或離開螢幕時,在生命週期中歷經各種狀態。

為了管理生命週期,Fragment 會導入 LifecycleOwner,開放一個 Lifecycle 物件,讓您透過 getLifecycle() 方法進行存取。

每個可能的 Lifecycle 狀態都會顯示在 Lifecycle.State 列舉中。

Lifecycle 上建構 Fragment,即可運用「利用生命週期感知元件處理生命週期」一文中提及的相關技術與類別。舉例來說,您可以使用生命週期感知元件在螢幕上顯示裝置的位置。此元件會在片段啟用時開始自動監聽,並在片段變成閒置狀態時停止監聽。

除了使用 LifecycleObserver 以外,Fragment 類別還具有可因應片段生命週期中各項變更的回呼方法。包括 onCreate()onStart()onResume()onPause()onStop()onDestroy()

片段檢視畫面具有獨立的 Lifecycle,且與片段的 Lifecycle 分別管理。片段則會保留 LifecycleOwner 的檢視畫面,可以透過 getViewLifecycleOwner()getViewLifecycleOwnerLiveData() 進行存取。有時候,生命週期感知元件只能在片段檢視畫面存在的情況下執行 (例如觀察只能顯示在畫面上的 LiveData)。在這類情況下,如果能取得檢視畫面的 Lifecycle 存取權限,就會很有幫助。

本主題會詳細說明 Fragment 的生命週期,解釋決定片段生命週期狀態的一些規則,以及 Lifecycle 狀態與片段生命週期回呼之間的關係。

片段與片段管理員

將片段執行個體化時,會以 INITIALIZED 狀態開始。您必須在 FragmentManager 中新增片段,才能在片段的剩餘生命週期中完成轉換。FragmentManager 負責判斷片段應處於哪個狀態,然後將其改變為該狀態。

除了片段的生命週期以外,FragmentManager 也會將片段附加到其代管活動上,並在片段停止使用時,將該片段從其上卸離。Fragment 類別有兩個回呼方法:onAttach()onDetach(),您可以在發生上述任一事件時覆寫以執行作業。

將片段新增至 FragmentManager 並附加至其代管活動時,系統會叫用 onAttach() 回呼。此時,片段處於啟用狀態,FragmentManager 會管理其生命週期狀態。這時,findFragmentById()FragmentManager 方法會傳回這個片段。

在任何生命週期狀態變更之前,系統都會呼叫 onAttach()

將片段從 FragmentManager 中移除並且從其代管活動上卸離時,系統會叫用 onDetach() 回呼。該片段將會停用,無法再使用 findFragmentById() 擷取。

在任何生命週期狀態變更之後,系統都會呼叫 onDetach()

請注意,這些回呼與 FragmentTransaction 方法 attach()detach() 無關。若要進一步瞭解這些方法,請參閱片段交易一文。

注意: 請避免在從 FragmentManager 移除後重複使用 Fragment 執行個體。雖然片段會處理自身的內部狀態清理作業,但您可能會不小心將自己的狀態,轉移到重複使用的執行個體中。

片段生命週期狀態及回呼

在判斷片段的生命週期狀態時,FragmentManager 會考量下列事項:

  • 片段的最大狀態取決於 FragmentManager。片段不能超過其 FragmentManager 狀態的進度。
  • 作為 FragmentTransaction 的一部分,可以使用 setMaxLifecycle() 設定片段最高的生命週期狀態。
  • 片段的生命週期狀態不得大於其父項。例如,父項片段或活動必須在其子項片段之前開始。同樣地,子項片段必須在父項片段或活動之前停止。

注意: 請避免使用 <fragment> 標記新增使用 XML 的片段,因為 <fragment> 標記允許片段不再受限於 FragmentManager 的狀態。請改用 FragmentContainerView 來新增使用 XML 的片段。

image.png

<figcaption style="box-sizing: inherit; font-size: 14px; margin-top: -4px;">圖 1. 片段 Lifecycle 狀態及其在片段的生命週期回呼和片段的檢視畫面 Lifecycle 之間的關係。</figcaption>

圖 1 顯示了每個片段的 Lifecycle 狀態,以及這些片段與片段的生命週期回呼和片段的檢視畫面 Lifecycle 之間的關係。

一個片段會在生命週期中不斷演進,因此狀態會往上或向下移動。例如,新增至返回堆疊頂端的片段會從 CREATED 上移至 STARTED 再到 RESUMED。反之,如果片段從返回堆疊中移除,就會從狀態中向下移動,從 RESUMEDSTARTEDCREATED 並最後到 DESTROYED

向上狀態轉換

在生命週期狀態中往上移動時,片段會先為新狀態呼叫相關的生命週期回呼。完成這個回呼後,相關的 Lifecycle.Event 就會透過片段的 Lifecycle 傳送至觀察者,如果完成執行個體化,會接著依序顯示片段的檢視畫面 Lifecycle

已建立的片段

片段達到 CREATED 狀態後,就會新增至 FragmentManager,並且呼叫了 onAttach() 方法。

這裡適合透過片段 SavedStateRegistry 還原所有與片段本身相關的儲存狀態。請注意,目前系統尚未建立片段的檢視畫面,與片段檢視畫面相關的所有狀態只能在建立檢視畫面後還原。

此轉換作業會叫用 onCreate() 回呼。回呼也會收到一個 savedInstanceState Bundle 引數,其中包含之前由 onSaveInstanceState() 儲存的任何狀態。請注意,savedInstanceState 會在第一次建立片段時提供 null 值,但即使您並未覆寫 onSaveInstanceState(),對後續的重新建立來說也絕不會是空值。詳情請參閱以片段儲存狀態一節。

已建立的片段及初始化的檢視畫面

只有在 Fragment 提供有效的 View 執行個體時,系統才會建立片段的檢視畫面 Lifecycle。在多數情況下,您可以使用接收 @LayoutId片段建構函式,讓系統在適當的時間自動加載檢視畫面。您也可以將 onCreateView() 覆寫,透過程式輔助加載或建立片段的檢視畫面。

只有在片段的檢視畫面以非空值 View 執行個體化時,該 View 才能設定在片段上,可以使用 getView() 擷取。接著,getViewLifecycleOwnerLiveData() 會更新為最新的 INITIALIZED LifecycleOwner,以對應片段的檢視畫面。此時也會呼叫 onViewCreated() 生命週期回呼。

這也是適合用來設定檢視畫面初始狀態的地方,您可開始觀察 LiveData 執行個體,其回呼會更新片段的檢視畫面,並在片段的檢視畫面中對任何 RecyclerViewViewPager2 的執行個體設定配接器。

已建立的片段及檢視畫面

建立片段的檢視畫面後,若有先前的檢視畫面,系統就會將其狀態還原,該檢視畫面的 Lifecycle 則改為 CREATED 狀態。檢視畫面生命週期的擁有者也會將 ON_CREATE 事件發送給其觀察者。在這裡,您應該還原任何其他與片段檢視畫面有關的狀態。

此轉換作業也會叫用 onViewStateRestored() 回呼。

已開始的片段及檢視畫面

強烈建議您將生命週期感知元件連結到片段的 STARTED 狀態,因為此狀態可以確保片段的檢視畫面可以使用(如果已產生),而且能夠在片段的子項 FragmentManager 上安全執行 FragmentTransaction。如果片段的檢視畫面不是空值,則在片段的 Lifecycle 移至 STARTED 後,片段檢視畫面的 Lifecycle 就會立即移至 STARTED

片段變成 STARTED 時,就會叫用 onStart() 回呼。

注意: ViewPager2 等元件會將畫面外的片段 Lifecycle 上限設為 STARTED

重新啟用的片段及檢視畫面

片段顯示時,所有 AnimatorTransition 效果皆已完成,該片段已可供使用者使用。片段的 Lifecycle 會更改為 RESUMED 狀態,並叫用 onResume() 回呼。

轉換至 RESUMED 是一個適當的訊號,表示使用者現在可以與片段互動。非 RESUMED 的片段就不應手動將焦點設定於自己的檢視畫面,或是嘗試處理輸入方法的顯示

向下狀態轉換

當片段往下移至較低的生命週期狀態時,如果發生執行個體化現象,片段的檢視畫面 Lifecycle 會將相關的 Lifecycle.Event 發送給觀察者,接著才是片段的 Lifecycle。片段的生命週期事件發送後,該片段會呼叫相關的生命週期回呼。

已開始的片段及檢視畫面

使用者開始離開片段時,而片段仍持續顯示,則片段及其檢視畫面的 Lifecycle 會回到 STARTED 狀態,並傳送 ON_PAUSE 事件給其觀察者。片段接著會叫用其 onPause() 回呼。

已建立的片段及檢視畫面

片段停止顯示後,片段及其檢視畫面的 Lifecycle 就會進入 CREATED 狀態,並發送 ON_STOP 活動給其觀察者。此狀態轉換不僅會因為父項活動或片段停止而觸發,也會在父項活動或片段儲存狀態時觸發。這個動作可確保在儲存片段狀態之前就叫用 ON_STOP 事件。這使得 ON_STOP 事件會是最後安全地為子項 FragmentManager 執行 FragmentTransaction的時間點。

如圖 2 所示,onStop() 回呼的順序和以 onSaveInstanceState() 儲存狀態會因 API 層級而異。針對 API 級別 28 之前的所有 API 級別,onSaveInstanceState() 會在 onStop() 之前叫用。針對 API 級別 28 以上的 API 級別,呼叫順序則相反。

image.png

<figcaption style="box-sizing: inherit; font-size: 14px; margin-top: -4px;">圖 2。onStop()onSaveInstanceState() 的呼叫順序差異。</figcaption>

建立的片段及刪除的檢視畫面

完成所有離開的動畫及轉換後,片段檢視畫面會從視窗卸離,片段的檢視畫面 Lifecycle 就會移至 DESTROYED 狀態,並將 ON_DESTROY 事件發送給觀察者。片段接著會叫用其 onDestroyView() 回呼。此時,片段的檢視畫面已到達生命週期尾端,getViewLifecycleOwnerLiveData() 會傳回 null 值。

此時,應該移除片段檢視畫面的所有參照,讓片段的檢視畫面能夠成為無用項目收集。

刪除的片段

如果片段遭移除,或是 FragmentManager 被刪除,則片段的 Lifecycle 就會移至 DESTROYED 狀態,並將 ON_DESTROY 事件傳送給其觀察者。片段接著會叫用其 onDestroy() 回呼。此時,片段已到達生命週期終點。

其他資源

若要進一步瞭解片段生命週期,請參閱下列其他資源。

指南

網誌

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容