Praktik terbaik navigasi untuk project multi-modul

Grafik navigasi dapat terdiri dari kombinasi berikut:

  • Tujuan tunggal, seperti tujuan <fragment>.
  • Grafik bertingkat yang mengenkapsulasi kumpulan tujuan terkait.
  • Elemen <include>, yang memungkinkan Anda menyematkan file grafik navigasi lain seolah-olah file tersebut disusun bertingkat.

Fleksibilitas ini memungkinkan Anda menggabungkan grafik navigasi yang lebih kecil untuk membentuk grafik navigasi lengkap aplikasi, meskipun grafik navigasi yang lebih kecil tersebut disediakan oleh modul terpisah.

Untuk contoh dalam topik ini, setiap modul fitur difokuskan pada satu fitur dan menyediakan satu grafik navigasi yang mengenkapsulasi semua tujuan yang diperlukan untuk mengimplementasikan fitur tersebut. Dalam aplikasi produksi, Anda mungkin memiliki banyak sub-modul pada tingkat yang lebih rendah yang merupakan detail implementasi dari modul fitur tingkat lebih tinggi ini. Setiap modul fitur ini disertakan, baik secara langsung maupun tidak langsung, ke dalam modul app Anda. Contoh aplikasi multi-modul yang digunakan dalam dokumen ini memiliki struktur berikut:

grafik dependensi untuk contoh aplikasi sampel multi-modul
tujuan awal aplikasi contoh
Gambar 1. Arsitektur aplikasi dan tujuan awal untuk aplikasi contoh.

Setiap modul fitur adalah unit mandiri dengan grafik navigasi dan tujuannya sendiri. Modul app bergantung pada modul masing-masing, yang menambahkannya sebagai detail implementasi dalam file build.gradle, seperti yang ditunjukkan:

Groovy

dependencies {
    ...
    implementation project(":feature:home")
    implementation project(":feature:favorites")
    implementation project(":feature:settings")

Kotlin

dependencies {
    ...
    implementation(project(":feature:home"))
    implementation(project(":feature:favorites"))
    implementation(project(":feature:settings"))

Peran modul app

Modul app bertanggung jawab menyediakan grafik lengkap untuk aplikasi Anda dan menambahkan NavHost ke UI. Dalam grafik navigasi modul app, Anda dapat mereferensikan grafik library menggunakan <include>. Meskipun penggunaan <include> secara fungsional sama dengan penggunaan grafik bertingkat, <include> mendukung grafik dari modul project lain atau dari project library, seperti yang ditunjukkan dalam contoh berikut:

<?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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/home_nav_graph">

    <include app:graph="@navigation/home_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />
</navigation>

Setelah library disertakan dalam grafik navigasi tingkat atas, Anda dapat membuka grafik library sesuai kebutuhan. Misalnya, Anda dapat membuat tindakan untuk membuka grafik setelan dari fragmen di grafik navigasi, seperti yang ditunjukkan:

<?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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/home_nav_graph">

    <include app:graph="@navigation/home_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />

    <fragment
        android:id="@+id/random_fragment"
        android:name="com.example.android.RandomFragment"
        android:label="@string/fragment_random" >
        <!-- Launch into Settings Navigation Graph -->
        <action
            android:id="@+id/action_random_fragment_to_settings_nav_graph"
            app:destination="@id/settings_nav_graph" />
    </fragment>
</navigation>

Jika beberapa modul fitur perlu mereferensikan sekumpulan tujuan umum, seperti grafik login, Anda tidak boleh menyertakan tujuan umum tersebut ke dalam grafik navigasi setiap modul fitur. Sebagai gantinya, tambahkan tujuan umum tersebut ke grafik navigasi modul app. Selanjutnya, setiap modul fitur dapat membuka berbagai modul fitur untuk membuka tujuan umum tersebut.

Pada contoh sebelumnya, tindakan menentukan tujuan navigasi @id/settings_nav_graph. ID ini mengacu pada tujuan yang ditentukan dalam grafik @navigation/settings_navigation. yang disertakan.

Navigasi tingkat atas di modul aplikasi

Komponen Navigation menyertakan class NavigationUI. Class ini berisi metode statis yang mengelola navigasi dengan panel aplikasi atas, panel navigasi, dan navigasi bawah. Jika tujuan tingkat atas aplikasi Anda terdiri dari elemen UI yang disediakan oleh modul fitur, modul app adalah tempat yang alami untuk menempatkan navigasi tingkat atas dan elemen UI. Karena modul aplikasi bergantung pada modul fitur yang berkolaborasi, semua tujuannya dapat diakses dari kode yang ditentukan dalam modul aplikasi Anda. Ini berarti Anda dapat menggunakan NavigationUI untuk menautkan tujuan ke item menu jika ID item cocok dengan ID tujuan.

Dalam gambar 2, contoh modul app menentukan BottomNavigationView dalam aktivitas utamanya. ID item menu di menu cocok dengan ID grafik navigasi di grafik library:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/home_nav_graph"
        android:icon="@drawable/ic_home"
        android:title="Home"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/favorites_nav_graph"
        android:icon="@drawable/ic_favorite"
        android:title="Favorites"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/settings_nav_graph"
        android:icon="@drawable/ic_settings"
        android:title="Settings"
        app:showAsAction="ifRoom" />
</menu>

Untuk mengizinkan NavigationUI menangani navigasi bawah, panggil setupWithNavController() dari onCreate() di class aktivitas utama Anda, seperti yang ditunjukkan dalam contoh berikut:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController

    findViewById<BottomNavigationView>(R.id.bottom_nav)
            .setupWithNavController(navController)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    NavHostFragment navHostFragment =
            (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);

    NavigationUI.setupWithNavController(bottomNav, navController);
}

Setelah kode ini diterapkan, NavigationUI akan membuka grafik library yang sesuai saat pengguna mengklik item navigasi bawah.

Perlu diingat bahwa, umumnya menjadi praktik yang buruk bagi modul aplikasi Anda jika memiliki dependensi yang kuat pada tujuan tertentu yang disematkan secara spesifik dalam grafik navigasi modul fitur Anda. Dalam sebagian besar kasus, Anda ingin agar modul aplikasi hanya mengetahui titik entri ke grafik navigasi yang disematkan atau disertakan (ini juga berlaku di luar modul fitur). Jika perlu menautkan ke tujuan sangat spesifik di dalam grafik navigasi library, cara yang lebih disukai untuk melakukannya adalah dengan menggunakan deep link. Penggunaan deep link juga merupakan satu-satunya cara bagi library untuk membuka tujuan di grafik navigasi library lain.

Membuka modul fitur

Pada waktu kompilasi, modul fitur independen tidak dapat saling melihat, sehingga Anda tidak dapat menggunakan ID untuk membuka tujuan di modul lain. Sebagai gantinya, gunakan deep link untuk membuka langsung ke tujuan yang terkait dengan deep link implisit.

Melanjutkan contoh sebelumnya, bayangkan Anda perlu membuka tujuan yang disusun bertingkat di modul :feature:settings dari tombol di modul :feature:home. Anda dapat melakukannya dengan menambahkan deep link ke tujuan di grafik navigasi setelan, seperti yang ditampilkan:

<?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"
    android:id="@+id/settings_nav_graph"
    app:startDestination="@id/settings_fragment_one">

    ...

    <fragment
        android:id="@+id/settings_fragment_two"
        android:name="com.example.google.login.SettingsFragmentTwo"
        android:label="@string/settings_fragment_two" >

        <deepLink
            app:uri="android-app://example.google.app/settings_fragment_two" />
    </fragment>
</navigation>

Lalu, tambahkan kode berikut ke onClickListener tombol di fragmen layar utama:

Kotlin

button.setOnClickListener {
    val request = NavDeepLinkRequest.Builder
        .fromUri("android-app://example.google.app/settings_fragment_two".toUri())
        .build()
    findNavController().navigate(request)
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        NavDeepLinkRequest request = NavDeepLinkRequest.Builder
            .fromUri(Uri.parse("android-app://example.google.app/settings_fragment_two"))
            .build();
        NavHostFragment.findNavController(this).navigate(request);
    }
});

Tidak seperti bernavigasi menggunakan ID tujuan atau tindakan, Anda dapat bernavigasi ke URI apa pun dalam grafik apa pun, bahkan di seluruh modul.

Saat bernavigasi menggunakan URI, data sebelumnya tidak direset. Perilaku ini tidak seperti navigasi deep link eksplisit, yang mengganti data sebelumnya saat bernavigasi.