Kami terbang melalui modul: Navigasi dalam aplikasi multi-modul dengan Jetpack


Hampir setiap proyek yang tumbuh cepat atau lambat mulai melihat ke arah arsitektur multimodular. Pengembang tidak ingin menunggu sampai seluruh proyek dipasang kembali, ketika hanya satu fitur yang telah diubah. Multimodularity membantu mengisolasi fitur aplikasi dari satu sama lain, sehingga mengurangi waktu pembuatan. Tetapi isolasi ini memberlakukan beberapa batasan pada ruang lingkup komponen. Ketika kami menggunakan navigasi dari Jetpack dalam proyek dengan satu modul, grafik navigasi tersedia dari paket aplikasi apa pun, kami selalu dapat secara eksplisit menentukan tindakan yang harus dilakukan NavController, dan juga memiliki akses ke host global jika ada fragmen yang bersarang di dalam proyek. Tetapi ketika ada banyak modul, muncul pertanyaan: di mana membuat grafik navigasi, bagaimana mengaksesnya dan bagaimana agar tidak bingung dalam ketergantungan modul-modul tersebut. Kami akan membicarakan semua ini di bawah potongan.


Grafik navigasi


Yang paling penting untuk diingat ketika mendesain aplikasi multi-modul adalah dependensi. Ketergantungan dalam pohon modul ketergantungan harus diarahkan dalam satu arah.



Modul yang paling tergantung pada aplikasi multi-modul adalah selalu modul aplikasi. Dia tahu hampir semua modul lainnya. Dalam aplikasi, DI biasanya diimplementasikan menggunakan berbagai kerangka kerja. Menggunakan dependensi modul aplikasi ini, Anda dapat mengimplementasikan grafik navigasi dari host utama di dalamnya.


Anda harus selalu ingat bahwa modul aplikasi harus mengimplementasikan fungsionalitas sesedikit mungkin, karena itu adalah yang paling tergantung dan hampir semua perubahan dalam proyek akan menghasilkan pemasangan kembali modul aplikasi.


Bicara itu murah. Tunjukkan kodenya


Dan segera menjadi contoh nyata. Kasus kami: titik masuk ke aplikasi adalah layar splash, ia menentukan layar mana yang akan dituju: fungsi utama atau otorisasi. Dari layar otorisasi, hanya ada transisi ke fungsi utama. Seperti biasa, kami membuat grafik navigasi - tidak ada yang rumit.



Akses ke navigasi di dalam modul


Ketika tiba saatnya untuk melakukan transisi dari satu layar ke layar dalam modul lain, muncul pertanyaan - bagaimana?


Memang, di dalam modul fitur tidak ada akses ke grafik navigasi untuk mendapatkan id tindakan yang harus dijalankan NavController.


Ini diselesaikan dengan menerapkan DI menggunakan antarmuka. Alih-alih modul fitur tergantung pada grafik global navigasi dari modul aplikasi, kami akan membuat antarmuka dan menyebutnya WhatToNavCommandProvider, variabel-variabelnya adalah perintah navigasi.


SplashNavCommandProvider.kt


interface SplashNavCommandProvider { val toAuth: NavCommand val toMain: NavCommand } 

Antarmuka penyedia perintah itu sendiri akan diimplementasikan dalam modul aplikasi, dan kelas perintah navigasi akan memiliki bidang yang sama dengan argumen untuk metode NavController.navigate


NavCommand.kt


 data class NavCommand( val action: Int, var args: Bundle? = null, val navOptions: NavOptions? = null ) 

Mari kita lihat tampilannya dalam praktik. Dari layar splash, 2 transisi dimungkinkan: ke layar otorisasi dan ke layar fungsional utama. Dalam modul splash, buat antarmuka:


SplashNavCommandProvider.kt


 interface SplashNavCommandProvider { val toAuth: NavCommand val toMain: NavCommand } 

Dalam modul aplikasi, kami membuat implementasi antarmuka ini dan menggunakan kerangka kerja di (saya punya Belati), kami menyediakannya melalui antarmuka splash ke modul.


SplashNavCommandProviderImpl.kt - implementasi CommandProvider


 class SplashNavCommandProviderImpl @Inject constructor() : SplashNavCommandProvider { override val toAuth: NavCommand = NavCommand(R.id.action_splashFragment_to_authFragment) override val toMain: NavCommand = NavCommand(R.id.action_splashFragment_to_mainFragment) } 

SplashNavigationModule.kt - modul DI untuk menyediakan dependensi


 @Module interface SplashNavigationModule { @Binds fun bindSplashNavigator(impl: SplashNavCommandProviderImpl): SplashNavCommandProvider } 

AppActivityModule.kt - modul DI utama aplikasi


 @Module interface AppActivityModule { @FragmentScope @ContributesAndroidInjector( modules = [ SplashNavigationModule::class ] ) fun splashFragmentInjector(): SplashFragment … } 

Dalam modul splash, kami mengimplementasikan implementasi dalam MV (di sini) baik Presenter atau ViewModel ...


SplashViewModel.kt


 class SplashViewModel @Inject constructor( private val splashNavCommandProvider: SplashNavCommandProvider ) ... 

Ketika logika layar menganggap bahwa sudah waktunya untuk beralih ke layar lain, kami mentransfer perintah ke fragmen kami dan menginformasikan bahwa kami perlu beralih ke layar lain.


Mungkin untuk mengimplementasikan implementasi SplashNavCommandProvider langsung ke fragmen, tetapi kemudian kita kehilangan kemampuan untuk menguji navigasi.


Dalam fragmen itu sendiri, untuk menyelesaikan transisi, Anda perlu mendapatkan NavController. Jika layar saat ini bukan fragmen bersarang, maka kita cukup mendapatkan NavController dengan metode findNavController () dan memanggil metode navigasi di atasnya:


 findNavController().navigate(toMain) 

Anda dapat membuatnya sedikit lebih nyaman dengan menulis ekstensi untuk fragmen


Framentmentxt.kt


 fun Fragment.navigate(navCommand: NavCommand) { findNavController().navigate(navCommand.action, navCommand.args, navCommand.navOptions) } 

Kenapa hanya untuk fragmen? Karena saya menggunakan pendekatan SingleActivity, jika Anda memiliki beberapa di antaranya, maka Anda dapat membuat ekstensi juga untuk Aktivitas.


Maka navigasi di dalam fragmen akan terlihat seperti ini


 navigate(toMain) 

Fragmen bersarang


Navigasi dalam fragmen bersarang dapat terdiri dari dua jenis:


  • Transisi dalam wadah bersarang
  • Masuk ke dalam wadah satu atau lebih tingkat lebih tinggi. Misalnya, host aktivitas global

Dalam kasus pertama, semuanya sederhana, ekstensi yang kami tulis di atas cocok untuk kami. Dan untuk menyelesaikan transisi dalam kasus kedua, Anda perlu mendapatkan NavController dari host yang diinginkan. Untuk melakukan ini, Anda harus mendapatkan id host ini di dalam modul. Karena hanya modul yang mengimplementasikan grafik navigasi dari host ini yang memiliki akses kepadanya, kami akan membuat dependensi dan mengimplementasikannya dalam modul fitur, di mana akses ke NavController tertentu diperlukan, melalui Belati.


Modul GlobalHostModule.kt - DI untuk menyediakan dependensi id host global


 @Provides @GlobalHost fun provideGlobalHostId(): Int = R.id.host_global 

AppActivityModule.kt - modul DI utama aplikasi


 @FragmentScope @ContributesAndroidInjector( modules = [ GlobalHostModule::class, ProfileNavigationModule::class, ... ] ) fun profileKnownFragmentInjector(): ProfileKnownFragment 

Menanamkan ketergantungan host id dalam fragmen


 @Inject @GlobalHost var hostId = 0 

Saat fragmen disarangkan, ada baiknya membuat Qualifier untuk setiap host atau menggunakan Qualifier yang Ada Dinamai sehingga Dagger memahami int yang akan disediakan.


Globalhost.kt


 @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalHost 

Setelah id dependensi dari host yang diinginkan diperoleh dalam fragmen, Anda bisa mendapatkan NavController oleh id host. Kami akan meningkatkan ekstensi kami untuk kemampuan melakukan transisi dalam wadah apa pun:


Framentmentxt.kt


 fun Fragment.navigate(navCommand: NavCommand, hostId: Int? = null) { val navController = if (hostId == null) { findNavController() } else { Navigation.findNavController(requireActivity(), hostId) } navController.navigate(navCommand.action, navCommand.args, navCommand.navOptions) } 

Kode dalam cuplikan


 navigate(toAuth, hostId) 

Ini adalah hal-hal penting dalam navigasi menggunakan Jetpack dalam arsitektur multi-modul. Jika Anda memiliki pertanyaan, maka saya akan dengan senang hati menjawabnya di komentar :)

Source: https://habr.com/ru/post/id475104/


All Articles