Android官方架構元件Navigation:大巧不工的Fragment管理框架

卻把清梅嗅發表於2019-02-01

本文已授權 微信公眾號 玉剛說@任玉剛)獨家釋出。

爭取打造 Android Jetpack 講解的最好的部落格系列

Android Jetpack 實戰篇

學習契機

在不久前的Google 2018 I/O大會上,Google正式推出了AndroidJetpack ——這是一套元件、工具和指導,可以幫助開發者構建出色的 Android 應用,這其中就包含了去年推出的 Lifecycle, ViewModel, LiveData 以及 Room。除此之外,AndroidJetpack 還隆重推出了一個新的架構元件:Navigation

從名字來看,我翻譯它叫導航, 我們來看看Google官方對它的描述:

今天,我們宣佈推出Navigation元件,作為構建您的應用內介面的框架,重點是讓單 Activity 應用成為首選架構。利用Navigation元件對 Fragment 的原生支援,您可以獲得架構元件的所有好處(例如生命週期和 ViewModel),同時讓此元件為您處理 FragmentTransaction 的複雜性。此外,Navigation元件還可以讓您宣告我們為您處理的轉場。它可以自動構建正確的“向上”和“返回”行為,包含對深層連結的完整支援,並提供了幫助程式,用於將導航關聯到合適的 UI 小部件,例如抽屜式導航欄和底部導航。

拋開比較性的話題不談(StoryBoard VS Navigation?),Navigation的釋出讓我意識到 這是一個契機,我覺得我有必要花時間去深入瞭解它——既能 學習新的技術及理念 ,同時又能 查漏補缺,完善自己的Android知識體系(Fragment的管理)

Android官方架構元件Navigation:大巧不工的Fragment管理框架

這件事立即被我列上日程,過去的一週,我閒暇之際仔細研究了 Navigation, 並略有心得,我嘗試寫下本文,在總結的同時,希望能夠給後來的朋友們一些 系統性的指導建議 。如果可能,我甚至希望這篇文章能夠做到:

本文不是詳細的API說明文件,但僅通過閱讀本文,能夠對 Navigation 有一個系統性地學習—— 瞭解它,理解它,最後搞懂它

這對讀寫雙方都是 一次挑戰。完成它的第一步是做到:知道Navigation這個導航元件 怎麼用

瞭解Navigation

1.官方文件

官方文件 永遠是最接近 正確核心理念 的參考資料 —— 在不久之後,本文可能會因為框架本身API的迭代更新而 毫無意義,但官方文件不會,即使在最惡劣的風暴中,它依然是最可靠的 指明燈

developer.android.com/topic/libra…

其次,一個好的Demo能夠起到重要的啟發作用, 這裡我推薦 Google實驗室 的這個Sample:

專案地址:github.com/googlecodel… 專案教程:codelabs.developers.google.com/codelabs/an…

這個教程Demo的優勢在於,官方為這個Demo提供了 一系列詳細的教程,通過一步步,引導學習每一個類或者元件的應用場景,最終完全上手 Navigation

因為剛剛釋出的原因,目前Navigation的中文教程 極其匱乏,許多資料的查閱可能需要開發者 自備梯子。不過請不必擔心,本文會力爭做到比其它同類文章講解的 更加全面

2.Sample展示

我寫了一個Navigation的sample,它最終的效果是這樣:

sample.gif

這是3個簡單的Fragment之間跳轉的情景,經過 轉場動畫 的修飾,它們之前的切換非常 流暢自然。在展示的最後,我們可以看到,Fragment2 -> Fragment1的時候,實際上是由 使用者 點選手機Back鍵 觸發的。

專案結構圖如下,這可以幫你儘快瞭解sample的結構:

Android官方架構元件Navigation:大巧不工的Fragment管理框架

我把這個sample的原始碼託管在了我的github上,你可以通過 點我檢視原始碼

3.嘗試使用Navigation

Navigation目前僅AndroidStudio 3.2以上版本支援,如果您的版本不足3.2,請點此下載預覽版AndroidStudio

首先介紹Navigation的使用:

無論是否認可,我們都必須承認,Google已經在嘗試讓Kotlin上位,無論是今年IO大會的 資料展示,還是官方文件上的 程式碼示例片段,亦或是Google最新 開源Demo的原始碼,使用語言清一色 Kotlin,本文亦然。

Android官方架構元件Navigation:大巧不工的Fragment管理框架

① 在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不同,沒有其它區別
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

Android官方架構元件Navigation:大巧不工的Fragment管理框架

開啟導航檢視檔案,我們可以在AndroidStudio 3.2版本上,進行視覺化編輯,包括選擇新增Fragment,或者拖拽,連線Fragment:

Android官方架構元件Navigation:大巧不工的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屬性內包名的正確宣告。

⑤ 編輯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。

⑥ 最後,配置不同Fragment對應的跳轉事件

class MainPage1Fragment : Fragment() {
     //隱藏了onCreateView()方法的實現,下同
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btn.setOnClickListener {
            //點選跳轉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 {
            //點選跳轉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 並非是通過原生的 FragmentManagerFragmentTransaction 進行控制的。而是通過以下API進行的控制:

  • Navigation.findNavController(params).navigateUp()
  • Navigation.findNavController(params).navigate(actionId)

到這裡,Navigation最基本的使用就已經講解完畢了。您可以通過執行預覽和示例 基本一致 的效果,如果遇到問題,或者有疑問,可以點我檢視原始碼

理解Navigation

我對於 通過部落格歸納總結 的學習方式已近兩年,我不斷反思,一篇優秀的文章不僅是做到 完整敘述,同時,它更應該體現的是 對思路的整理簡潔乾淨地闡述它們

做到這點並不容易,首先需要做到的就是 不要僅侷限於API的使用——最初的學習中,通過上面的程式碼,我已經 實現了Fragment的導航。但是,上面的程式碼中,除了Activity 和 Fragment,其它的東西我一個都不認識。

我感覺很難受, 所謂 行百里路半九十,別說九十,這個Navigation,我一竅不通

Android官方架構元件Navigation:大巧不工的Fragment管理框架

僅有上述示例程式碼毫無意義,通過它們,更應該將其理解為 入門;接下來我們需要做到 瞭解每一個類的職責,理解框架設計者的思想

我們先思考這樣一個問題:如果讓我們實現一個Fragment的導航庫,首先要實現什麼?

1.NavGraphFragment:導航介面的容器

答案近在眼前。

即使我們使用原生的API,想展示一個Fragment,我們首先也需要 定義一個容器承載它。以往,它可能是一個 RelativeLayout 或者 FrameLayout,而現在,它被替換成了 NavGraphFragment

這也就說明了,我們為什麼要往Activity的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內部例項化了一個FrameLayout, 作為ViewGroup的載體,導航並展示其它Fragment

除此之外,你 應當注意 到在layout檔案中,它還宣告瞭另外兩個屬性:

app:defaultNavHost="true" app:navGraph="@navigation/nav_graph_main"

app:defaultNavHost="true"這個屬性意味著你的NavGraphFragment將會 攔截系統Back鍵的點選事件(因為系統的back鍵會直接關閉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:宣告導航結構圖

NavGraphFragment作為Activity導航的 容器 ,然後,其 app:navGraph 屬性指向一個navigation_graph的xml檔案,以宣告其 導航的結構

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的根節點下,我們需要處理這樣一個屬性:

app:startDestination="@id/page1Fragment"

Destination 是一個很關鍵的單詞,它的直譯是 目的地app:startDestination屬性便是宣告這個id對應的 Destination 會被作為 預設佈局 載入到Activity中。這也就說明了,為什麼我們的sample,預設會顯示 MainPage1Fragment

現在,我們的app預設展示了MainPage1Fragment, 那麼接下來,我們如何實現跳轉邏輯的處理呢?

3.Action標籤:宣告導航的行為

我們宣告瞭這樣一個Action標籤,這是一個 導航的行為

<action
    android:id="@+id/action_page2"
    app:destination="@id/page2Fragment" />
複製程式碼

app:destination的屬性,宣告瞭這個行為導航的 destination(目的地),我們可以看到,它會指印跳轉到 id 為 page2Fragment 的Fragment(也就是 MainPage2Fragment)。

android:id 這個id作為Action唯一的 標識,在Fragment的某個點選事件中,我們通過id指向對應的行為,就像這樣:

btn.setOnClickListener {
       //點選跳轉page2Fragment
       Navigation.findNavController(it).navigate(R.id.action_page2)
}
複製程式碼

此外,Navigation還提供了一個 app:popUpTo 屬性,它的作用是宣告導航行為返回到 id對應的Fragment,比如,直接從Page3 返回到 Page1。

此外,Navigation 對導航行為還提供了 轉場動畫 的支援,它可以通過程式碼這樣實現:

<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之間 引數傳遞 的支援,以及對SubNavigation標籤的支援,以方便開發者在xml檔案中 複用fragment標籤 ——甚至是對 Deep Link 的支援,但這些擴充功能本文不再敘述。

4.Fragment:通過程式碼宣告導航

其實在3中我們已經講解了導航程式碼的使用,我們以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 架構中 最重要的核心類,我們所有的導航行為都由 NavController 處理,這個我們後面再講。

我們通過獲取 NavController,然後呼叫 NavController. navigate()方法進行導航。

Android官方架構元件Navigation:大巧不工的Fragment管理框架

我們更多情況下通過傳入ActionId,指定對應的 導航行為 ;同時可以通過傳入Bundle以 資料傳遞;或者是再傳入一個 NavOptions配置更多(比如 轉場動畫,它也可以通過這種方式進行程式碼的動態配置)。

NavController.navigate()方法更多時候應用在 向下導航 或者 指定向上導航(比如Page3 直接返回 Page1,跳過返回Page2的這一步);如果我們處理back事件,我們應該使用 NavController. navigateUp()

恭喜您,已經能夠遊刃有餘的使用Navigation!

恭喜您,您已對 Navigation 十分熟悉,並能通過熟練使用其 暴露的API,靈活地處理您應用中的 頁面導航 行為。

我美滋滋的在個人履歷上填上了這樣一條:

  • 熟練使用Google官方元件Navigation實現Fragment的管理,並掌握其原理

面試官對此十分感動,然後讓我談談 對它架構設計的一些個人觀點

Android官方架構元件Navigation:大巧不工的Fragment管理框架

到了這一步,我們算得上是 API的搬運工 ,我們已經 瞭解每一個類的職責,還沒有完全 理解框架設計者的思想

徹底搞懂Navigation

在我們熟悉Navigation的API之後,我們整裝待發,準備 原始碼級攻克 Navigation。

正如我所說的,在這之前,您首先需要達到 熟練使用Navigation,本文地初衷並非是 一步到位,而是嘗試 循序漸進

1.對原始碼分析說NO

宣告 —— 我拒絕 大段大段地原始碼分析,我認為這種行為 嚴重降低 了文章的 質量深度

我花了一些時間繪製了 Navigation的UML類圖,我堅信,這種方式能幫助你我 更深刻的理解 Navigation的整體架構:

UML類圖

讓我們換個角度,我們的身份不再是 原始碼的觀眾,而是 架構的設計者

2. 設計 NavHostFragment

NavHostFragment 應當有兩個作用:

  • 作為Activity導航介面的載體
  • 管理並控制導航的行為

前者的作用我們已經說過了,我們通過在NavHostFragment的建立時,為它建立一個對應的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 的持有者抽象為一個 介面,以便於以後的擴充。

於是我們創造了 NavHost 介面,並讓NavHostFragment實現了這個介面:

public interface NavHost {

    NavController getNavController();
}
複製程式碼

為了保證導航的 安全,NavHostFragment 在其 作用域 內,理應 有且僅有一個NavController 的例項

這裡我們駐足一下,請注意API的設計,似乎 Navigation.findNavController(View),引數中傳遞任意一個 view的引用似乎都可以獲取 NavController——如何保證 NavController 的區域性單例呢?

事實上,findNavController(View)內部實現是通過 遍歷 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工程師的野心,因為現在我們就是他,從擴充性的角度考慮,Navigation是一個導航框架,今後可能 並非只為Fragment導航

我們應該為要將導航的 Destination 抽象出來,這個類叫做 NavDestination ——無論 Fragment 也好,Activity 也罷,只要實現了這個介面,對於NavController 來講,他們都是 **Destination(目標點)**而已。

對於不同的 NavDestination 來講,它們之間的導航方式是不同的,這完全有可能(比如Activity 和 Fragment),如何根據不同的 NavDestination 進行不同的 導航處理 呢?

4. NavDestination 和 Navigator

有同學說,我可以這樣設計,通過 instanceof 關鍵字,對 NavDestination 的型別進行判斷,並分別做出處理,比如這樣:

if (destination instanceof Fragment) {
  //對應Fragment的導航
} else if (destination instanceof Activity) {
  //對應Activity的導航
}
複製程式碼

這是OK的,但是不夠優雅,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.能夠例項化對應的 NavDestination
  • 2.能夠指定導航
  • 3.能夠後退導航

你看,我的 NavController 獲取了所有 NavDestination 的Class物件,但是我不負責它 如何例項化 ,也不負責 如何導航 ,也不負責 如何後退 ——我僅僅持有向上的引用,然後呼叫它的介面方法,它的實現我不關心。

FragmentNavigator為例,我們來看看它是如何執行的職責:

public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
    //省略大量非關鍵程式碼,請以實際程式碼為主!
  
    @Override
    public boolean popBackStack() {
        return mFragmentManager.popBackStackImmediate();
    }

    @NonNull
    @Override
    public Destination createDestination() {
        // 實際執行了好幾層,但核心程式碼如下,通過反射例項化Fragment
        Class<? extends Fragment> clazz = getFragmentClass();
        return  clazz.newInstance();
    }

    @Override
    public void navigate(@NonNull Destination destination, @Nullable Bundle args,
                            @Nullable NavOptions navOptions) {
        // 實際上還是通過FragmentTransaction進行的跳轉處理
        final Fragment frag = destination.createFragment(args);
        final FragmentTransaction ft = mFragmentManager.beginTransaction();
        ft.replace(mContainerId, frag);
        ft.commit();
        mFragmentManager.executePendingTransactions();
    }
}
複製程式碼

不同的 Navigator 對應不同的 NavDestinationFragmentNavigator 對應的是 FragmentNavigator.Destination,你可以把他理解為案例中的 Fragment ,有興趣的朋友可以自己研究一下。

5.至此

至此,Navigation 整體的架構設計 也已經通過 UML類圖 + 設計的角度分析 的方式學習完了。

當然,Navigation 還有很多其它的類我沒有去闡述,它們已經無法阻攔你我的腳步。

我更建議 讀者在這之後,能夠嘗試自己閱讀原始碼,通過借鑑上文中的 UML類圖,當然,自己通過思路的整理,自己繪製出一份,會對理解它更有幫助。

總結

Navigation 是一個優秀的庫,這從API上無法體現,因為它和其它優秀的三方 Fragment 管理庫 都能達到 固定的目標

並且,隨著技術的不斷髮展,它們也早晚會被歷史所淹沒,我們能夠做到的,就是使用API的同時,學習它的思想,並收為己用

---------------------------------------------------廣告分割線-------------------------------------------------------

關於我

Hello,我是卻把清梅嗅,如果您覺得文章對您有價值,歡迎 ❤️,也歡迎關注我的部落格或者Github

如果您覺得文章還差了那麼點東西,也請通過關注督促我寫出更好的文章——萬一哪天我進步了呢?

相關文章