Android開發總結之Fragment
1 Fragment是什麼?
Fragment
碎片,我的理解就是一個具有生命週期的容器控制元件,只不過Fragment是寄生在Activity上的,是由Activity來管理,而不是像Activity一樣由系統來管理的。所以用法上Fragment比Activity更輕巧靈活。
因其比Activity更節省記憶體,便於切換,使用頻率不低於四大元件,所以俗稱"第五大元件"
目前大多數使用v4包下的Fragment.
1.1 談談Fragment與Activity的區別
共同點:都有生命週期方法,而且很類似。
不同點:
back stack
2 怎樣新增Fragment?
2.1 靜態新增
在Activity或Fragment的xml佈局檔案裡新增 fragment
標籤
特點:新增方式簡單但靈活性不夠,預設就將Fragment和Activity的檢視繫結在一起。
2.2 動態新增
java程式碼new 一個Fragment,再通過在Activity/Fragment的FragmentManager/ChildFragmentManager,管理該Fragment( 載入 , 移除 , 替換 ).
具體步驟:
- 1 建立待新增的碎片例項
- 2 獲取getSupportFragmentManager()方法得到
- 3 開啟一個事務,通過呼叫beginTransaction()方法開啟。
- 4 向容器內新增或替換碎片,一般使用repalce()方法實現,需要傳入容器的id和待新增的碎片例項
- 5 提交事務,呼叫commit()方法來完成
方式比較複雜,但也是唯一一種可以在執行時控制fragment的方式。
2.2.1 FragmentTransaction操作
getSupportFragmentManager().getFragments()
:獲取當前Activity ViewTree上有Fragment的Fragment列表。
attach(), detach() add(), remove(), show(), hide(), replace()
- 其中
attach()
和detach()
不是很常用,呼叫detach()
之後, fragment實際的生命週期會走到onDestroyView(),也就是在Activity的ViewTree上被移除了,也意味著getSupportFragmentManager().getFragments()
不包括此Fragment了,而且重要的是fragment的isAdded()
方法之後會一直返回false
,但Fragment不會走onDestroy()和onDetach(), 即fragment本身還並沒有被銷燬, 只是view被銷燬了。所以此時的Fragment處於detached狀態,可通過fragment的isDetached()
方法判斷,此時再呼叫attach()
,Fragment又會重新onCreatView()
->onViewCreated()
->onActivityCreated()
-
show()
和hide()
只是設定fragment的visible,會觸發fragment的onHiddenChanged()
回撥,不會影響其生命週期,就跟View的setVisible(bool)
一樣 -
add()
和remove()
,影響fragment生命週期的程度比attach()
和detach()
更深,也就是調remove()
後,fragment會走onDestroy()
、onDetach()
,fragment會被銷燬。add()
是新加入一個fragment,會從onCreate()
開始走,所以同一個fragment例項是不能被add到Activity兩次的。 -
replace()
,相當於remove()
+add()
,新的fragment將取代在容器佈局中的fragment, 如果沒有,將直接新增新的fragment
3 Fragment的生命週期
- Fragment的生命週期方法比Activity多
- 因為Fragment生命週期是由Activity管的,所以不同情況下使用Fragment,Fragment的生命週期方法有不同的執行流程。
我總結的 基本原則 : Activty已死(不可見),那麼在它上的Fragment也必須死。Activity活著(可見),Fragment可以隨便死,隨便生 ,有點像地球和地球上生物的生死關係 ^-^
。
3.1 在Fragment始終依附在Activity上的情況下:
[圖片上傳失敗...(image-d502bc-1551951782199)]
我理解為Fragment的對應週期都要比Activity晚一點,畢竟“先有母雞,才有小雞”嘛,Activity created對應Fragment的onAttach(),onCreate(),onCreateView(),onActivityCreated(),好了這段時間都是建立建立,然後介面進入可見不可互動狀態,即onStart(),然後介面進入可見可互動狀態,也就是onResume();當然反過來,Activty stop了,destroy了,Fragment也要跟著銷燬。
3.2 在Activity可見,FragmentManager動態管理Fragment的的情況下:
略,2.2.1已講。
4 FragmentPageAdapter和FragmentPageStateAdapter的區別
- FragmnetPageAdapter在每次切換頁面時,只是將Fragment進行分離,適合頁面較少的Fragment使用以儲存一些記憶體,對系統記憶體不會多大影響
- FragmentPageStateAdapter在每次切換頁面的時候,是將Fragment進行回收,適合頁面較多的Fragment使用,這樣就不會消耗更多的記憶體
5 Fragment通訊
Fragment同Activity,Fragment之間通訊一般都是為了滿足介面資料同步的業務需求。
5.1 Fragment與Activity通訊
5.1.1 Fragment 傳訊息給Activity
5.1.1.1 Fragment 傳訊息給與它atttach()的ctivity
1 ( 常用 )Fragment通過getActivity()獲取Activity例項,然後強轉為對應的Activity類或介面,最後呼叫。前提是getActivity()不為null,即Fragment還沒有detach()。

image
2 Fragment通過發廣播,然後Activity接收實現。
3 Fragment通過事件匯流排(EventBus,RxBus)發訊息,然後Activity接收實現。
5.1.1.2 Fragment 傳訊息給與其他Activity
1 跳轉到另一個Activity傳:startActivityForResult([攜帶資料的intent])
2 Fragment通過發廣播,然後Activity接收實現。
3 Fragment通過事件匯流排(EventBus,RxBus)發訊息,然後Activity接收實現。
5.1.2 Activity傳訊息給Fragment
一般是傳訊息給和自己attanch的fragment.
1 構造Fragment時傳,一般Fragment都要自己定義個工廠方法。
2 通過FragmentManager和Fragment ID獲取Fragment例項,再呼叫Fragment實現。
3 不推薦用廣播和EventBus
5.2 Fragment與Fragment通訊
1 Fragment通過getActivity調Activity,然後Activity通過FragmentManager找到目標Fragment,然後呼叫目標Fragment
2 廣播或者事件匯流排:原理是觀察者與被觀察者模式,即多個Fragment統一觀察某一個物件
5.3 不同的Fragment,Activity觀察同一個ViewModel來實現同步,也是一種通訊策略
6 BackStack回退棧
Activity有任務棧,Fragment也有類似的棧,稱為回退棧,由FragmentManager管理。預設情況下,Fragment事務是不會加入回退棧的,如果想將Fragment事務加入回退棧,則可以加入addToBackStack(tag)。如果沒有加入回退棧,則使用者點選返回按鈕會直接將Activity出棧;如果加入了回退棧,則使用者點選返回鍵會回滾Fragment事務。
BackStack回退棧一般用於我理解的“縱向”導航,就是從一個介面一層一層深入到裡面的介面。
不適合“橫向”導航,也就是在一個介面裡橫向切換不同檢視。
7 Fragment重疊現象
一般是在用add()、show()、hide()這三個方法管理Fragment切換的導航業務場景。
出現原因:Activity被重建,當然重建原因一是系統配置發生改變,二是記憶體不足被回收。
重現方法:
- 系統配置發生改變的,最方便的是用模擬器模擬,模擬器會有一個旋轉螢幕的按鈕,比如網易的momo模擬器就行。
- 模擬Activity記憶體不夠:
- 1.手機的 "設定" - "開發者選項" - 開啟"不保留活動"(主要用於模擬Activity被及時回收)
- 2.把 app 切換到後臺,再重新開啟,通過點按不同的 tab 來切換 Fragment
具體原因:
- 比如當裝置選擇螢幕時,Activity會被銷燬並重新建立,並且在銷燬之前執行了onSaveInstanceState(Bundle outState)這個方法。這個方法會儲存activity的檢視層(View Hierarchy)資訊,其中就包括新增過的fragment,當activity被重新建立時,一方面會初始化使用者自定義的一些變數,另一方面也會重現之前儲存的Fragment,重新導航時右add了一遍新的fragment例項,所以造成重疊。
解決方案一:
- 1.給每個 Fragment 加一個 Tag
- 2.在 onCreate(Bundle savedInstanceState) 中判斷 Bundle savedInstanceState 是否不為空
- 3.不為空則進行 find Tag,重新給幾個 frament 賦值
這樣子仍是對之前儲存的 fragment 操作,成功解決了重疊的問題。
具體方案可參考: fragment重疊的完美解決方案
解決方案二:
讓Activity異常銷燬時,不儲存資料
//解決重疊,方法1@Override protectedvoidonSaveInstanceState(Bundle outState) { //如果用以下這種做法則不儲存狀態,再次進來的話會顯示預設tab //super.onSaveInstanceState(outState); }
不過這種方案不推薦,假如Activity就是要儲存比如非fragment view的資料,好恢復銷燬前的狀態呢?
8 擴充套件雜談
為什麼 JakeWharton 建議:App 只要用到一個 Activity ?
咦,一個App只用一個Activity,Flutter就是這樣的啊,要是這樣還不如用flutter。
另外我覺得一個App只用一個Activity,其它用Fragment這樣的方案也只能在業務量小的應用用,畢竟Activity和Fragment之間的關係還是蠻複雜的。