Jetpack路由元件學習:深入理解功能強大的Navigation架構之接管系統的返回操作
1.前言
不管你之前用沒用過Jetpack Navigation元件,但是或多或少你也可能聽說過它。它是Jetpack庫中的一個路由元件。此刻你的腦海中可能會浮現阿里ARouter框架。如果你熟悉ARouter但是對Navigation比較陌生,那麼你先簡單把它們聯絡在一起,有個直觀的感受。
「如果你對ARouter和Navigation都不太熟悉,沒關係,並不影響你對本文的閱讀。」
Navigation支援Activity、Fragment、Dialog的路由跳轉,功能非常強大。此刻靈魂拷問一下。
❝ 你知道Navigation是如何實現Activity、Fragment元件的跳轉嗎? 你知道Navigation如何接管系統返回鍵操作的? ❞
第一個問題,在不看原始碼的情況下,我們大概也能略猜一二,Activity的路由是透過startActivity(intent)方法來實現的,Fragment的路由是透過FragmentTransaction的replace方法來實現的。關於真正的跳轉原理,之後會出專文講解,敬請關注本公眾號,及時獲取更文通知。
第二個問題,在不深入理解原始碼的情況下,如果被問到,我會直接被問懵。難道是重寫onBackPressed()?
好了,不賣關子了,Navigation元件是透過FragmentTransaction.setPrimaryNavigationFragment()方法接管系統返回鍵操作的。有原始碼為證:
//NavHostFragment.javapublic void onAttach(@NonNull Context context) { super.onAttach(context); // TODO This feature should probably be a first-class feature of the Fragment system, // but it can stay here until we can add the necessary attr resources to // the fragment lib. if (mDefaultNavHost) { getParentFragmentManager().beginTransaction() .setPrimaryNavigationFragment(this) .commit(); } }
NavHostFragment從字面意思理解,它是Navigation元件的導航宿主元件。Fragment元件如果想要在Navigation框架中實現路由功能必須滿足兩個條件:
- Fragment必須依附在NavHostFragment上
- NavHostFragment必須成為PrimaryNavigationFragment(後文簡稱:主導航Fragment)
「行文至此,你可能有點懵,但是沒關係,你只需要知道有主導航Fragment這個概念就行,接著往下看」
本文的主角是setPrimaryNavigationFragment(),它是Fragment框架中的方法,它並不是什麼新鮮玩意,它跟隨Fragment框架一起釋出的。但是很弔詭地是,翻遍所有的技術社群,都很難找到講解它的文章。但是它真的很重要,它是Navigation元件的基礎設施(水和電)。
要想深入理解Navigation實現原理,必須對Fragment實現原理深入瞭解。FragmentTransaction、BackStackRecord、FragmentManager等概念必須成竹於胸。
說回NavHostFragment
//NavHostFragment.javapublic class NavHostFragment extends Fragment implements NavHost { public void onAttach(@NonNull Context context) { super.onAttach(context); // TODO This feature should probably be a first-class feature of the Fragment system, // but it can stay here until we can add the necessary attr resources to // the fragment lib. if (mDefaultNavHost) { getParentFragmentManager().beginTransaction() .setPrimaryNavigationFragment(this) .commit(); } } }
很簡單的幾行程式碼,卻藏著不少資訊:
NavHostFragment是導航宿主Fragment,要實現路由跳轉的Fragment都是它的child Fragment。這裡就涉及到childFragmentManager,parentFragmentManager等知識了。
mDefaultNavHost是要設定成true,NavHostFragment才能成為主導航Fragment。
從註釋中,可以看出,setPrimaryNavigationFragment()雖然很不起眼,它即將升級為Fragment的一類公民了(first-class)。
2. FragmentManager 回退棧
棧是一種很簡單的資料結構。它的特點是“後進先出”。在FragmentManager中回退棧定義如下:
public abstract class FragmentManager{ ArrayList<BackStackRecord> mBackStack; }
在FragmentTransaction中有addToBackStack(String name)方法,可以將某個方法加入回退棧中。
class FragmentActvitiy1: AppCompatActivity() { fun addFragmentNotAddToBackStack() { supportFragmentManager.commit { setReorderingAllowed(true) add<AFragment>(R.id.top_fragment_container_view) } supportFragmentManager.commit { setReorderingAllowed(true) add<BFragment>(R.id.top_fragment_container_view) } } }
class FragmentActvitiy2: AppCompatActivity() { fun addFragmentNotAddToBackStack() { supportFragmentManager.commit { setReorderingAllowed(true) add<AFragment>(R.id.top_fragment_container_view) addToBackStack(null) } supportFragmentManager.commit { setReorderingAllowed(true) add<BFragment>(R.id.top_fragment_container_view) addToBackStack(null) } } }
上述兩段程式碼,唯一的區別就是FragmentActivity1沒有呼叫addToBackStack()方法,而FragmentActivity2呼叫了。
FragmentActivity1 按返回鍵效果如下:
FragmentActivity2 按返回鍵效果如下:
3. FragmentManager處理返回原理
「handleOnBackPressed()」 處理邏輯如下:
當回退棧中有記錄時,呼叫popBackStackImmediate(),該方法呼叫popBackStackImmediate(String name, int id, int flags)
「程式碼1處」 就是處理當前FragmentManager有主導航Fragment時的返回場景。如果主導航Fragment不為空時,交由childManager處理返回。如果childMananger攔截了返回鍵處理則返回,否則繼續讓當前FragmentManager處理。具體場景,後文詳解。
「程式碼2處」 popBackStackState(ArrayListrecords, ArrayListisRecordPop, String name, int id, int flags)的作用是將回退棧中的FragmentTransaction(BackStackRecord)放到records集合中,以備後用。
出棧分為4種情況(簡單起見,id不考慮了,否則根據排列組合有8中情況)
case | name | POP_BACK_STACK_INCLUSIVE |
---|---|---|
case1 | null | 0 |
case2 | not null | 0 |
case3 | not null | 1 |
case4 | null | 1 |
POP_BACK_STACK_INCLUSIVE = 1時表示,根據name找到返回棧裡面的BackStackRecord,一起出棧。
假設有回退棧如下。我們來走下四種case。
「case->popBackStack(null,0)」
「case2->popBackStack(“s2”,0)」
「case3->popBackStack(“s2”,1)」
「case4->popBackStack(null,1)」
「程式碼3處」 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop)真正執行出棧操作。最終執行到executeOpsTogether方法。
4. setPrimaryNavigationFragment
經過前面那麼多的鋪墊,終於來到講解本文主角setPrimaryNavigationFragment了(下文簡稱:“主導航Fragment”)。前文例子,我們都是基於FragmentActivity平鋪Fragment場景講解的。如果Fragment巢狀Fragment,該如何處理返回棧呢。“主導航Fragment”就是為了解決巢狀Fragment而設計的。前文講到“Fragment必須依附在NavHostFragment上”,其實就是巢狀Fragment了。
從上圖我們看到有三種角色。HostActivity,HostFragment,Child Fragment(s)。
HostFragment就是透過成為“主導航Fragment”,接管Activity的處理返回操作,並且將返回操作交由ChildFragment(s)去處理。作用可謂“承上啟下”。
文字描述始終有點晦澀難懂。上場景圖解。
分兩種場景。
Case | HostFragment |
---|---|
Case1 | 不成為主導航Fragment |
Case2 | 成為主導航Fragment |
「Case1 虛擬碼如下」
「Case2 虛擬碼如下」
「返回場景如下」
「Case1」
「Case2」
5. 總結
Navigation架構是在Fragment框架基礎上構建的。由於Fragment本身比較複雜。所以要想深入理解Navigation的設計思想,就需要對Fragment和Navigation都很熟練,這是一個痛點。
當然“紙上得來終覺淺,絕知此事要躬行”。 我寫得再詳細,也有遺漏的細節未能表達出來,你讀得再認真,不實踐一把,你也未必能夠真正明白所有的技術要點。所以建議閱讀完文章,深入原始碼實踐一把,把文章中的知識,轉化成自己的東西。有任何問題,歡迎一起交流。
最後不用多說,相信大家都有一個共識:無論什麼行業,最牛逼的人肯定是站在金字塔端的人。所以,想做一個牛逼的程式設計師,那麼就要讓自己站的更高,成為技術大牛並不是一朝一夕的事情,需要時間的沉澱和技術的積累。
關於這一點,在我當時確立好Android方向時,就已經開始梳理自己的成長路線了,包括技術要怎麼系統地去學習,都列得非常詳細。
這裡最後分享耗時一年多整理的一系列Android學習資源:
Android原始碼解析、Android第三方庫原始碼筆記、Android進階架構師七大專題學習、歷年BAT面試題解析包、Android大佬學習筆記
等等。
這些內容均免費分享給大家,需要完整版的朋友, 。或者點選 【 】 檢視獲取方式。
最後,希望文章對你有幫助。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2785457/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android Jetpack 架構元件之 NavigationAndroidJetpack架構元件Navigation
- Jetpack架構元件學習(1)——LifeCycle的使用Jetpack架構元件
- Jetpack架構元件學習(3)——Activity Results API使用Jetpack架構元件API
- Jetpack架構元件學習(2)——ViewModel和Livedata使用Jetpack架構元件ViewLiveData
- Android Jetpack架構元件(七)之WorkManagerAndroidJetpack架構元件
- Jetpack 之 Navigation 初探JetpackNavigation
- Jetpack Compose學習(11)——Navigation頁面導航的使用JetpackNavigation
- Android Jetpack Navigation 深入體驗報告AndroidJetpackNavigation
- 【AAC 系列二】深入理解架構元件的基石:Lifecycle架構元件
- Android官方架構元件Navigation:大巧不工的Fragment管理框架Android架構元件NavigationFragment框架
- 深入理解分散式系統中的快取架構(下)分散式快取架構
- 【AAC 系列四】深入理解架構元件:ViewModel架構元件View
- 【AAC 系列三】深入理解架構元件:LiveData架構元件LiveData
- Android Jetpack 之Navigation Architecture Component使用AndroidJetpackNavigation
- Jetpack Compose學習(7)——MD樣式架構元件Scaffold及導航底部選單Jetpack架構元件
- 深入理解lambada架構架構
- 深入理解flutter的程式碼結構:元件Flutter元件
- 架構思考:不靠譜的元件與可靠的系統架構元件
- 【許曉笛】重新理解EOS的系統架構架構
- Android Jetpack Navigation基本使用AndroidJetpackNavigation
- 初學 Android 架構元件之 LifecycleAndroid架構元件
- 初學 Android 架構元件之 ViewModelAndroid架構元件View
- 深入理解需求分析的目標(C系架構設計法)架構
- 系統架構設計師學習(二)系統架構設計師緒論架構
- Kafka 概述:深入理解架構Kafka架構
- Laravel深入學習5 – 應用架構Laravel應用架構
- 理解分散式系統中的快取架構(下)分散式快取架構
- 理解分散式系統中的快取架構(上)分散式快取架構
- 深入理解計算機系統-學習筆記 (1)計算機筆記
- Android JetPack 簡介及 Work Manager 和 Navigation 元件詳述 | Google 開發者大會AndroidJetpackNavigation元件Go
- Jetpack Navigation----原始碼解析JetpackNavigation原始碼
- 深入理解計算機系統學習- 計算機系統漫遊計算機
- 深入理解ES6--10.增強的陣列功能陣列
- Android_Jetpack:Paging元件之BoundaryCallback的使用AndroidJetpack元件
- java學習之深入構造器Java
- 系統架構設計師學習之路(31)架構
- 【學習筆記】CSS深入理解之margin筆記CSS
- 【學習筆記】CSS深入理解之overflow筆記CSS