本文已授權(quán) 微信公眾號 玉剛說 (@任玉剛)獨家發(fā)布。
前言
在不久前的Google 2018 I/O大會上,Google正式推出了AndroidJetpack ——這是一套組件、工具和指導,可以幫助開發(fā)者構(gòu)建出色的 Android 應用,這其中就包含了去年推出的 Lifecycle, ViewModel, LiveData 以及 Room。除此之外,AndroidJetpack 還隆重推出了一個新的架構(gòu)組件:Navigation。
從名字來看,我翻譯它叫導航, 我們來看看Google官方對它的描述:
今天,我們宣布推出Navigation組件,作為構(gòu)建您的應用內(nèi)界面的框架,重點是讓單 Activity 應用成為首選架構(gòu)。利用Navigation組件對 Fragment 的原生支持,您可以獲得架構(gòu)組件的所有好處(例如生命周期和 ViewModel),同時讓此組件為您處理 FragmentTransaction 的復雜性。此外,Navigation組件還可以讓您聲明我們?yōu)槟幚淼霓D(zhuǎn)場。它可以自動構(gòu)建正確的“向上”和“返回”行為,包含對深層鏈接的完整支持,并提供了幫助程序,用于將導航關(guān)聯(lián)到合適的 UI 小部件,例如抽屜式導航欄和底部導航。
拋開比較性的話題不談(StoryBoard VS Navigation?),Navigation的發(fā)布讓我意識到 這是一個契機,我覺得我有必要花時間去深入了解它——既能 學習新的技術(shù)及理念 ,同時又能 查漏補缺,完善自己的Android知識體系(Fragment的管理)。

這件事立即被我列上日程,過去的一周,我閑暇之際仔細研究了 Navigation, 并略有心得,我嘗試寫下本文,在總結(jié)的同時,希望能夠給后來的朋友們一些 系統(tǒng)性的指導建議 。如果可能,我甚至希望這篇文章能夠做到:
本文不是詳細的API說明文檔,但僅通過閱讀本文,能夠?qū)?Navigation 有一個系統(tǒng)性地學習—— 了解它,理解它,最后搞懂它。
這對讀寫雙方都是 一次挑戰(zhàn)。完成它的第一步是做到:知道Navigation這個導航組件 怎么用。
了解Navigation
1.官方文檔
官方文檔 永遠是最接近 正確 和 核心理念 的參考資料 —— 在不久之后,本文可能會因為框架本身API的迭代更新而 毫無意義,但官方文檔不會,即使在最惡劣的風暴中,它依然是最可靠的 指明燈:
https://developer.android.com/topic/libraries/architecture/navigation/
其次,一個好的Demo能夠起到重要的啟發(fā)作用, 這里我推薦 Google實驗室 的這個Sample:
項目地址:https://github.com/googlecodelabs/android-navigation
項目教程:https://codelabs.developers.google.com/codelabs/android-navigation/#0
這個教程Demo的優(yōu)勢在于,官方為這個Demo提供了 一系列詳細的教程,通過一步步,引導學習每一個類或者組件的應用場景,最終完全上手 Navigation。
因為剛剛發(fā)布的原因,目前Navigation的中文教程 極其匱乏,許多資料的查閱可能需要開發(fā)者 自備梯子。不過請不必擔心,本文會力爭做到比其它同類文章講解的 更加全面。
2.Sample展示
我寫了一個Navigation的sample,它最終的效果是這樣:

這是3個簡單的Fragment之間跳轉(zhuǎn)的情景,經(jīng)過 轉(zhuǎn)場動畫 的修飾,它們之前的切換非常 流暢 且 自然。在展示的最后,我們可以看到,F(xiàn)ragment2 -> Fragment1的時候,實際上是由 用戶 點擊手機Back鍵 觸發(fā)的。
項目結(jié)構(gòu)圖如下,這可以幫你盡快了解sample的結(jié)構(gòu):

我把這個sample的源碼托管在了我的github上,你可以通過 點我查看源碼 。
3.嘗試使用Navigation
Navigation目前僅AndroidStudio 3.2以上版本支持,如果您的版本不足3.2,請點此下載預覽版AndroidStudio
首先介紹Navigation的使用:
無論是否認可,我們都必須承認,Google已經(jīng)在嘗試讓Kotlin上位,無論是今年IO大會的 數(shù)據(jù)展示,還是官方文檔上的 代碼示例片段,亦或是Google最新 開源Demo的源碼,使用語言清一色 Kotlin,本文亦然。

① 在Module下的build.gradle中添加以下依賴:
dependencies {
def nav_version = '1.0.0-alpha01'
implementation "android.arch.navigation:navigation-fragment:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"
}
② 新建三個Fragment:
//3個Fragment,它們除了layout不同,沒有其它區(qū)別
class MainPage1Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_main_page1, container, false)
}
}
class MainPage2Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_main_page2, container, false)
}
}
class MainPage3Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_main_page3, container, false)
}
}
③ 新建導航視圖文件(nav_graph)
在res目錄下新建navigation文件夾,然后新建一個navigation的resource文件,我叫它 nav_graph_main.xml :

打開導航視圖文件,我們可以在AndroidStudio 3.2版本上,進行可視化編輯,包括選擇新增Fragment,或者拖拽,連接Fragment:

④ 編輯導航視圖文件
我們打開Text標簽,進入xml編輯的頁面,并這樣配置:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/page1Fragment">
<fragment
android:id="@+id/page1Fragment"
android:name="com.qingmei2.samplejetpack.ui.main.MainPage1Fragment"
android:label="fragment_page1"
tools:layout="@layout/fragment_main_page1">
<action
android:id="@+id/action_page2"
app:destination="@id/page2Fragment" />
</fragment>
<fragment
android:id="@+id/page2Fragment"
android:name="com.qingmei2.samplejetpack.ui.main.MainPage2Fragment"
android:label="fragment_page2"
tools:layout="@layout/fragment_main_page2">
<action
android:id="@+id/action_page1"
app:popUpTo="@id/page1Fragment" />
<action
android:id="@+id/action_page3"
app:destination="@id/nav_graph_page3" />
</fragment>
<navigation
android:id="@+id/nav_graph_page3"
app:startDestination="@id/page3Fragment">
<fragment
android:id="@+id/page3Fragment"
android:name="com.qingmei2.samplejetpack.ui.main.MainPage3Fragment"
android:label="fragment_page3"
tools:layout="@layout/fragment_main_page3" />
</navigation>
</navigation>
注意:請保證fragment標簽下,android:name屬性內(nèi)包名的正確聲明。
⑤ 編輯MainActivity
在Activity中配置 Navigation 非常簡單,我們首先編輯Activity的布局文件,并在布局文件中添加一個 NavHostFragment :
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_main" />
</android.support.constraint.ConstraintLayout>
這是一個寬和高都 match_parent 的Fragment,它的作用就是 導航界面的容器。
這并不難以理解,我們需要在Activity中通過 Navigation 展示一系列的Fragment,但是我們需要告訴Navigation 和Activity,這一系列的 Fragment 展示在哪——NavHostFragment應運而生,我把它的作用歸納為 導航界面的容器。
這之后,在Activity中添加如下代碼:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onSupportNavigateUp() =
findNavController(this, R.id.my_nav_host_fragment).navigateUp()
}
onSupportNavigateUp()方法的重寫,意味著Activity將它的 back鍵點擊事件的委托出去,如果當前并非棧中頂部的Fragment, 那么點擊back鍵,返回上一個Fragment。
⑥ 最后,配置不同F(xiàn)ragment對應的跳轉(zhuǎn)事件
class MainPage1Fragment : Fragment() {
//隱藏了onCreateView()方法的實現(xiàn),下同
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn.setOnClickListener {
//點擊跳轉(zhuǎn)page2
Navigation.findNavController(it).navigate(R.id.action_page2)
}
}
}
class MainPage2Fragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn.setOnClickListener {
//點擊返回page1
Navigation.findNavController(it).navigateUp()
}
btn2.setOnClickListener {
//點擊跳轉(zhuǎn)page3
Navigation.findNavController(it).navigate(R.id.action_page3)
}
}
}
class MainPage3Fragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//點擊返回page2
btn.setOnClickListener { Navigation.findNavController(it).navigateUp() }
}
}
可以看到,我們對于Fragment 并非是通過原生的 FragmentManager 和 FragmentTransaction 進行控制的。而是通過以下API進行的控制:
- Navigation.findNavController(params).navigateUp()
- Navigation.findNavController(params).navigate(actionId)
到這里,Navigation最基本的使用就已經(jīng)講解完畢了。您可以通過運行預覽和示例 基本一致 的效果,如果遇到問題,或者有疑問,可以點我查看源碼 。
理解Navigation
我對于 通過博客歸納總結(jié) 的學習方式已近兩年,我不斷反思,一篇優(yōu)秀的文章不僅是做到 完整敘述,同時,它更應該體現(xiàn)的是 對思路的整理 并 簡潔干凈地闡述它們。
做到這點并不容易,首先需要做到的就是 不要僅局限于API的使用——最初的學習中,通過上面的代碼,我已經(jīng) 實現(xiàn)了Fragment的導航。但是,上面的代碼中,除了Activity 和 Fragment,其它的東西我一個都不認識。
我感覺很難受, 所謂 行百里路半九十,別說九十,這個Navigation,我一竅不通。

僅有上述示例代碼毫無意義,通過它們,更應該將其理解為 入門;接下來我們需要做到 了解每一個類的職責,理解框架設計者的思想。
我們先思考這樣一個問題:如果讓我們實現(xiàn)一個Fragment的導航庫,首先要實現(xiàn)什么?
1.NavGraphFragment:導航界面的容器
答案近在眼前。
即使我們使用原生的API,想展示一個Fragment,我們首先也需要 定義一個容器承載它。以往,它可能是一個 RelativeLayout 或者 FrameLayout,而現(xiàn)在,它被替換成了 NavGraphFragment。
這也就說明了,我們?yōu)槭裁匆鵄ctivity的layout文件中提前扔進去一個NavGraphFragment,因為我們需要導航的這些Fragment都展示在NavGraphFragment上面。
實際上它做了什么呢?來看一下NavGraphFragment的onCreateView()方法:
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
FrameLayout frameLayout = new FrameLayout(inflater.getContext());
frameLayout.setId(getId());
return frameLayout;
}
NavGraphFragment內(nèi)部實例化了一個FrameLayout, 作為ViewGroup的載體,導航并展示其它Fragment。
除此之外,你 應當注意 到在layout文件中,它還聲明了另外兩個屬性:
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_main"
app:defaultNavHost="true"這個屬性意味著你的NavGraphFragment將會 攔截系統(tǒng)Back鍵的點擊事件(因為系統(tǒng)的back鍵會直接關(guān)閉Activity而非切換Fragment),你同時 必須重寫 Activity的 onSupportNavigateUp() 方法,類似這樣:
override fun onSupportNavigateUp()
= findNavController(R.id.nav_host_fragment).navigateUp()
app:navGraph="@navigation/nav_graph_main"這個屬性就很好理解了,它會指向一個navigation_graph的xml文件,這之后,NavGraphFragment就會 導航并展示對應的Fragment。
在我們使用Navigation的第一步,我們需要:
在Activity的布局文件中顯示聲明NavGraphFragment,并配置 app:defaultNavHost 和 app:navGraph屬性。
2.nav_graph.xml:聲明導航結(jié)構(gòu)圖
NavGraphFragment作為Activity導航的 容器 ,然后,其 app:navGraph 屬性指向一個navigation_graph的xml文件,以聲明其 導航的結(jié)構(gòu)。
NavGraphFragment在 獲取 并 解析 完這個xml資源文件后,它首先需要知道的是:
類似APP的home界面,NavGraphFragment首先要導航到哪里?
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/page1Fragment">
<fragment
android:id="@+id/page1Fragment"
android:name="com.qingmei2.samplejetpack.ui.main.MainPage1Fragment"
android:label="fragment_page1"
tools:layout="@layout/fragment_main_page1">
<action
android:id="@+id/action_page2"
app:destination="@id/page2Fragment" />
</fragment>
//省略...
</navigation>
在navigation的根節(jié)點下,我們需要處理這樣一個屬性:
app:startDestination="@id/page1Fragment"
Destination 是一個很關(guān)鍵的單詞,它的直譯是 目的地。app:startDestination屬性便是聲明這個id對應的 Destination 會被作為 默認布局 加載到Activity中。這也就說明了,為什么我們的sample,默認會顯示 MainPage1Fragment。
現(xiàn)在,我們的app默認展示了MainPage1Fragment, 那么接下來,我們?nèi)绾螌崿F(xiàn)跳轉(zhuǎn)邏輯的處理呢?
3.Action標簽:聲明導航的行為
我們聲明了這樣一個Action標簽,這是一個 導航的行為:
<action
android:id="@+id/action_page2"
app:destination="@id/page2Fragment" />
app:destination的屬性,聲明了這個行為導航的 destination(目的地),我們可以看到,它會指印跳轉(zhuǎn)到 id 為 page2Fragment 的Fragment(也就是 MainPage2Fragment)。
android:id 這個id作為Action唯一的 標識,在Fragment的某個點擊事件中,我們通過id指向對應的行為,就像這樣:
btn.setOnClickListener {
//點擊跳轉(zhuǎn)page2Fragment
Navigation.findNavController(it).navigate(R.id.action_page2)
}
此外,Navigation還提供了一個 app:popUpTo 屬性,它的作用是聲明導航行為 將 返回到 id對應的Fragment,比如,直接從Page3 返回到 Page1。
此外,Navigation 對導航行為還提供了 轉(zhuǎn)場動畫 的支持,它可以通過代碼這樣實現(xiàn):
<action
android:id="@+id/confirmationAction"
app:destination="@id/confirmationFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
篇幅原因,這些anim的xml文件我并未展示在文中,如有需求,請參考Sample代碼。
其實Navigation 還提供了對Destination之間 參數(shù)傳遞 的支持,以及對SubNavigation標簽的支持,以方便開發(fā)者在xml文件中 復用fragment標簽 ——甚至是對 Deep Link 的支持,但這些拓展功能本文不再敘述。
4.Fragment:通過代碼聲明導航
其實在3中我們已經(jīng)講解了導航代碼的使用,我們以Page2為例,它包含了2個按鈕,分別對應 返回Page1 和 進入Page3 兩個事件:
btn.setOnClickListener {
Navigation.findNavController(it).navigateUp()
}
btn2.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_page3)
}
Navigation.findNavController(View) 返回了一個 NavController ,它是整個 Navigation 架構(gòu)中 最重要的核心類,我們所有的導航行為都由 NavController 處理,這個我們后面再講。
我們通過獲取 NavController,然后調(diào)用 NavController.navigate()方法進行導航。

我們更多情況下通過傳入ActionId,指定對應的 導航行為 ;同時可以通過傳入Bundle以 數(shù)據(jù)傳遞;或者是再傳入一個 NavOptions配置更多(比如 轉(zhuǎn)場動畫,它也可以通過這種方式進行代碼的動態(tài)配置)。
NavController.navigate()方法更多時候應用在 向下導航 或者 指定向上導航(比如Page3 直接返回 Page1,跳過返回Page2的這一步);如果我們處理back事件,我們應該使用 NavController.
navigateUp()。
恭喜您,已經(jīng)能夠游刃有余的使用Navigation!
恭喜您,您已對 Navigation 十分熟悉,并能通過熟練使用其 暴露的API,靈活地處理您應用中的 頁面導航 行為。
我美滋滋的在個人履歷上填上了這樣一條:
- 熟練使用Google官方組件Navigation實現(xiàn)Fragment的管理,并掌握其原理
面試官對此十分感動,然后讓我談談 對它架構(gòu)設計的一些個人觀點。

到了這一步,我們算得上是 API的搬運工 ,我們已經(jīng) 了解每一個類的職責,還沒有完全 理解框架設計者的思想。
徹底搞懂Navigation
在我們熟悉Navigation的API之后,我們整裝待發(fā),準備 源碼級攻克 Navigation。
正如我所說的,在這之前,您首先需要達到 熟練使用Navigation,本文地初衷并非是 一步到位,而是嘗試 循序漸進。
1.對源碼分析說NO
聲明 —— 我拒絕 大段大段地源碼分析,我認為這種行為 嚴重降低 了文章的 質(zhì)量 和 深度。
我花了一些時間繪制了 Navigation的UML類圖,我堅信,這種方式能幫助你我 更深刻的理解 Navigation的整體架構(gòu):

讓我們換個角度,我們的身份不再是 源碼的觀眾,而是 架構(gòu)的設計者。
2. 設計 NavHostFragment
NavHostFragment 應當有兩個作用:
- 作為Activity導航界面的載體
- 管理并控制導航的行為
前者的作用我們已經(jīng)說過了,我們通過在NavHostFragment的創(chuàng)建時,為它創(chuàng)建一個對應的FrameLayout作為 導航界面的載體:
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
FrameLayout frameLayout = new FrameLayout(inflater.getContext());
frameLayout.setId(getId());
return frameLayout;
}
我們都知道代碼設計應該遵循 單一職責原則,因此,我們應該將 管理并控制導航的行為 交給另外一個類,這個類的作用應該僅是 控制導航行為,因此我們命名為 NavController。
Fragment理應持有這個NavController的實例,并將導航行為 委托 給它,這里我們將 NavController 的持有者抽象為一個 接口,以便于以后的拓展。
于是我們創(chuàng)造了 NavHost 接口,并讓NavHostFragment實現(xiàn)了這個接口:
public interface NavHost {
NavController getNavController();
}
為了保證導航的 安全,NavHostFragment 在其 作用域 內(nèi),理應 有且僅有一個NavController 的實例。
這里我們駐足一下,請注意API的設計,似乎 Navigation.findNavController(View),參數(shù)中傳遞任意一個 view的引用似乎都可以獲取 NavController——如何保證 NavController 的局部單例呢?
事實上,findNavController(View)內(nèi)部實現(xiàn)是通過 遍歷 View樹,直到找到最底部 NavHostFragment 中的NavController對象,并將其返回的:
private static NavController findViewNavController(@NonNull View view) {
while (view != null) {
NavController controller = getViewNavController(view);
if (controller != null) {
return controller;
}
ViewParent parent = view.getParent();
view = parent instanceof View ? (View) parent : null;
}
return null;
}
3.設計 NavController
站在 設計者 的角度,NavController 的職責是:
- 1.對navigation資源文件夾下nav_graph.xml的 解析
- 2.通過解析xml,獲取所有 Destination(目標點)的 引用 或者 Class的引用
- 3.記錄當前棧中 Fragment的順序
- 3.管理控制 導航行為
NavController 持有了一個 NavInflater ,并通過 NavInflater 解析xml文件。
這之后,獲取了所有 Destination(在本文中即Page1Fragment , Page2Fragment , Page3Fragment ) 的 Class對象,并通過反射的方式,實例化對應的 Destination,通過一個隊列保存:
private NavInflater mInflater; //NavInflater
private NavGraph mGraph; //解析xml,得到NavGraph
private int mGraphId; //xml對應的id,比如 nav_graph_main
//所有Destination的隊列,用來處理回退棧
private final Deque<NavDestination> mBackStack = new ArrayDeque<>();
這看起來沒有任何問題,但是站在 設計者 的角度上,還略有不足,那就是,Navigation并非只為Fragment服務。
先不去吐槽Google工程師的野心,因為現(xiàn)在我們就是他,從拓展性的角度考慮,Navigation是一個導航框架,今后可能 并非只為Fragment導航。
我們應該為要將導航的 Destination 抽象出來,這個類叫做 NavDestination ——無論 Fragment 也好,Activity 也罷,只要實現(xiàn)了這個接口,對于NavController 來講,他們都是 Destination(目標點)而已。
對于不同的 NavDestination 來講,它們之間的導航方式是不同的,這完全有可能(比如Activity 和 Fragment),如何根據(jù)不同的 NavDestination 進行不同的 導航處理 呢?
4. NavDestination 和 Navigator
有同學說,我可以這樣設計,通過 instanceof 關(guān)鍵字,對 NavDestination 的類型進行判斷,并分別做出處理,比如這樣:
if (destination instanceof Fragment) {
//對應Fragment的導航
} else if (destination instanceof Activity) {
//對應Activity的導航
}
這是OK的,但是不夠優(yōu)雅,Google的方式是通過抽象出一個類,這個類叫做 Navigator :
public abstract class Navigator<D extends NavDestination> {
//省略很多代碼,包括部分抽象方法,這里僅闡述設計的思路!
//導航
public abstract void navigate(@NonNull D destination, @Nullable Bundle args,
@Nullable NavOptions navOptions);
//實例化NavDestination(就是Fragment)
public abstract D createDestination();
//后退導航
public abstract boolean popBackStack();
}
Navigator(導航者) 的職責很單純:
- 1.能夠?qū)嵗瘜?NavDestination
- 2.能夠指定導航
- 3.能夠后退導航
你看,我的 NavController 獲取了所有 NavDestination 的Class對象,但是我不負責它 如何實例化 ,也不負責 如何導航 ,也不負責
如何后退 ——我僅僅持有向上的引用,然后調(diào)用它的接口方法,它的實現(xiàn)我不關(guān)心。
以 FragmentNavigator為例,我們來看看它是如何執(zhí)行的職責:
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
//省略大量非關(guān)鍵代碼,請以實際代碼為主!
@Override
public boolean popBackStack() {
return mFragmentManager.popBackStackImmediate();
}
@NonNull
@Override
public Destination createDestination() {
// 實際執(zhí)行了好幾層,但核心代碼如下,通過反射實例化Fragment
Class<? extends Fragment> clazz = getFragmentClass();
return clazz.newInstance();
}
@Override
public void navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions) {
// 實際上還是通過FragmentTransaction進行的跳轉(zhuǎn)處理
final Fragment frag = destination.createFragment(args);
final FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.replace(mContainerId, frag);
ft.commit();
mFragmentManager.executePendingTransactions();
}
}
不同的 Navigator 對應不同的 NavDestination,FragmentNavigator 對應的是 FragmentNavigator.Destination,你可以把他理解為案例中的 Fragment ,有興趣的朋友可以自己研究一下。
5.至此
至此,Navigation 整體的架構(gòu)設計 也已經(jīng)通過 UML類圖 + 設計的角度分析 的方式學習完了。
當然,Navigation 還有很多其它的類我沒有去闡述,它們已經(jīng)無法阻攔你我的腳步。
我更建議 讀者在這之后,能夠嘗試自己閱讀源碼,通過借鑒上文中的 UML類圖,當然,自己通過思路的整理,自己繪制出一份,會對理解它更有幫助。
總結(jié)
Navigation 是一個優(yōu)秀的庫,這從API上無法體現(xiàn),因為它和其它優(yōu)秀的三方 Fragment 管理庫 都能達到 固定的目標。
并且,隨著技術(shù)的不斷發(fā)展,它們也早晚會被歷史所淹沒,我們能夠做到的,就是使用API的同時,學習它的思想,并收為己用。
--------------------------廣告分割線------------------------------
系列文章
爭取打造 Android Jetpack 講解的最好的博客系列:
- Android官方架構(gòu)組件Lifecycle:生命周期組件詳解&原理分析
- Android官方架構(gòu)組件ViewModel:從前世今生到追本溯源
- Android官方架構(gòu)組件LiveData: 觀察者模式領域二三事
- Android官方架構(gòu)組件Paging:分頁庫的設計美學
- Android官方架構(gòu)組件Paging-Ex:為分頁列表添加Header和Footer
- Android官方架構(gòu)組件Paging-Ex:列表狀態(tài)的響應式管理
- Android官方架構(gòu)組件Navigation:大巧不工的Fragment管理框架
- Android官方架構(gòu)組件DataBinding-Ex:雙向綁定篇
Android Jetpack 實戰(zhàn)篇:
關(guān)于我
Hello,我是卻把清梅嗅,如果您覺得文章對您有價值,歡迎 ??,也歡迎關(guān)注我的個人博客或者Github。
如果您覺得文章還差了那么點東西,也請通過關(guān)注督促我寫出更好的文章——萬一哪天我進步了呢?