Ini adalah seri artikel MAD Skills tentang Hilt! Dalam artikel ini, kita akan melihat mengapa injeksi dependensi (DI) sangatlah penting untuk aplikasi dan Hilt, yaitu solusi yang direkomendasikan Jetpack untuk DI di Android.
Jika Anda lebih menyukai konten ini dalam format video, lihat di sini:
Dengan mengikuti prinsip injeksi dependensi di aplikasi Android, Anda meletakkan dasar untuk arsitektur aplikasi yang baik. Hal ini membantu dalam penggunaan kembali kode, kemudahan pemfaktoran ulang, dan kemudahan pengujian! Pelajari lebih lanjut tentang manfaat DI di sini.
Saat membuat instance class dalam proyek, Anda bisa menggunakan grafik dependensi secara manual dengan memenuhi dependensi dan dependensi transitif yang dibutuhkan class.
Namun melakukannya secara manual setiap saat bisa menyebabkan kode boilerplate dan rawan error. Lihat contohnya, salah satu ViewModels yang kami miliki di iosched, aplikasi Google I/O open source. Dapatkah Anda membayangkan jumlah kode yang diperlukan untuk membuat FeedViewModel dengan dependensi dan dependensi transitifnya?
Ini bukanlah hal yang mudah, repetitif, dan kita bisa saja mendapatkan dependensi yang salah. Dengan menggunakan library injeksi dependensi, kita bisa mendapatkan manfaat DI tanpa harus menyediakan dependensi secara manual, karena library membuatkan semua kode yang diperlukan. Dan di sinilah Hilt berperan.
Hilt adalah library injeksi dependensi yang dikembangkan oleh Google untuk membantu Anda mendapatkan hasil maksimal dari praktik terbaik DI di aplikasi dengan melakukan semua kerja keras dan membuat semua boilerplate yang Anda perlukan.
Dengan menggunakan anotasi, Hilt membuatkan kode tersebut pada waktu kompilasi, sehingga membuatnya sangat cepat saat runtime. Hal ini dilakukan dengan menggunakan kekuatan Dagger, library DI JVM, dan Hilt dibangun di atasnya.
Hilt adalah solusi DI yang direkomendasikan Jetpack untuk aplikasi Android dan dilengkapi dengan fitur dan dukungan library Jetpack lainnya.
Semua aplikasi yang menggunakan Hilt harus berisi class Application yang dianotasikan dengan @HiltAndroidApp karena ini memicu pembuatan kode Hilt pada waktu kompilasi. Dan agar Hilt dapat memasukkan dependensi ke dalam aktivitas, aktivitas tersebut harus dianotasikan dengan @AndroidEntryPoint.
Untuk memasukkan dependensi, anotasikan variabel yang ingin dimasukkan dengan Hilt menggunakan @Inject. Semua variabel yang dimasukkan Hilt akan tersedia saat super.onCreate dipanggil.
Dalam contoh ini, kami memasukkan MusicPlayer ke dalam PlayActivity. Namun bagaimana Hilt tahu cara menyediakan instance tipe MusicPlayer? Yah, saat ini Hilt belum tahu caranya! Kita perlu memberi tahu Hilt cara melakukannya… dengan anotasi! tentu saja.
Menganotasikan konstruktor class dengan @Inject akan memberi tahu Hilt cara membuat instance class tersebut.
Hanya ini yang dibutuhkan untuk memasukkan dependensi ke dalam Activity! Cukup mudah! Kita mulai dengan contoh sederhana karena MusicPlayer tidak bergantung pada tipe lainnya. Namun jika kita memiliki dependensi lain yang diteruskan sebagai parameter, Hilt akan menanganinya dan memenuhi dependensi tersebut saat menyediakan instance MusicPlayer.
Ini sebenarnya, contoh yang sangat sederhana dan mudah. Namun jika Anda harus melakukan apa yang telah kami lakukan sejauh ini secara manual, bagaimana Anda akan melakukannya?
Saat melakukan DI secara manual, Anda bisa memiliki class container Dependensi yang bertanggung jawab untuk menyediakan tipe, dan mengelola siklus proses instance yang disediakannya. Kami menyederhanakannya sedikit di sini, itulah yang dilakukan Hilt di balik prosesnya!
Saat Anda memberikan anotasi pada Activity dengan @AndroidEntryPoint, container dependensi secara otomatis dibuat, dikelola, dan dikaitkan dengan PlayActivity. Mari kita panggil implementasi manualnya, PlayActivityContainer. Dengan menganotasikan MusicPlayer dengan @Inject, pada dasarnya kita memberi tahu container cara menyediakan instance tipe MusicPlayer.
Dan dalam Activity, kita perlu membuat instance container, dan mengisi dependensi aktivitas yang menggunakannya. Hal ini juga dilakukan oleh Hilt saat menganotasikan aktivitas dengan @AndroidEntryPoint.
Sejauh ini, kita melihat bahwa ketika @Inject digunakan untuk menganotasikan konstruktor class, ia memberi tahu Hilt cara menyediakan instance class tersebut. Dan saat menganotasikan variabel di class beranotasi @AndroidEntryPoint, Hilt memasukkan instance tipe tersebut ke dalam class.
@AndroidEntryPoint, yang bisa menganotasikan sebagian besar class framework Android, tidak hanya aktivitas, membuat instance container dependensi untuk class tersebut dan mengisi semua variabel beranotasi @Inject.
@HiltAndroidApp menganotasikan class Application, dan selain memicu pembuatan kode Hilt, juga membuat container dependensi yang terkait dengan class Application.
Setelah membahas dasar-dasar Hilt, mari kita perumit contohnya. Sekarang, MusicPlayer menggunakan dependensi dalam konstruktornya, MusicDatabase.
Oleh karena itu, kita harus memberi tahu Hilt cara menyediakan instance MusicDatabase. Jika tipenya adalah antarmuka atau Anda bukan pemilik class karena asalnya dari library, misalnya, Anda tidak bisa menganotasikan konstruktornya dengan @Inject!
Mari kita bayangkan menggunakan Room sebagai library persistensi di aplikasi. Kembali ke implementasi manual PlayActivityContainer, saat menyediakan MusicDatabase, dengan Room ini menjadi class abstrak, kita akan menjalankan beberapa kode saat menyediakan dependensi. Kemudian, saat menyediakan instance MusicPlayer, kita harus memanggil metode yang menyediakan atau memenuhi dependensi MusicDatabase.
Kita tidak perlu mengkhawatirkan dependensi transitif di Hilt, karena Hilt menghubungkan semua dependensi transitif secara otomatis. Namun, kita harus memberi tahu cara menyediakan instance tipe MusicDatabase. Untuk itu, kita menggunakan modul Hilt.
Modul Hilt adalah class yang dianotasikan dengan @Module. Dan di dalam class, kita bisa memiliki fungsi yang memberi tahu Hilt cara menyediakan instance tipe tertentu. Informasi ini juga dikenal oleh Hilt dengan sebutan binding dalam jargon Hilt.
Fungsi yang dianotasikan dengan @Provides memberi tahu Hilt cara menyediakan instance tipe MusicDatabase. Body berisi blok kode yang perlu dieksekusi Hilt, dan ini persis sama seperti yang dilakukan dalam implementasi manual kami.
Jenis nilai yang ditampilkan, MusicDatabase, menginformasikan Hilt tentang tipe yang disediakan fungsi ini. Dan parameter fungsi memberi tahu Hilt dependensi dari tipe yang sesuai, dalam hal ini, ApplicationContext yang sudah tersedia di Hilt. Kode tersebut memberi tahu Hilt cara menyediakan instance tipe MusicDatabase, atau dengan kata lain, kita memiliki binding untuk MusicDatabase.
Modul Hilt juga dianotasikan dengan anotasi @InstallIn yang menunjukkan container dependensi atau komponen letak informasi ini. Namun apa yang dimaksud dengan komponen? Mari kita bahas ini secara lebih detail.
Komponen adalah class yang dihasilkan Hilt yang bertanggung jawab untuk menyediakan instance tipe, seperti container yang telah kita program secara manual. Pada waktu kompilasi, Hilt melintasi grafik dependensi aplikasi Anda dan menghasilkan kode untuk menyediakan semua tipe dengan dependensi transitifnya.
Hilt menghasilkan Komponen, atau container dependensi, untuk sebagian besar class framework Android. Informasi, atau binding, dari setiap komponen disebarluaskan melalui hierarki komponen.
Jika binding MusicDatabase tersedia di SingletonComponent, yang sesuai dengan class Application, ia juga akan tersedia di komponen lainnya.
Komponen ini dihasilkan secara otomatis oleh Hilt pada waktu kompilasi, dan dibuat, dikelola, serta dihubungkan dengan class framework Android yang sesuai saat Anda menganotasikan class tersebut dengan @AndroidEntryPoint.
Anotasi @InstallIn untuk modul berguna mengontrol tempat menyediakan binding tersebut dan binding lain yang bisa mereka gunakan.
Kembali ke kode PlayActivityContainer yang kami buat secara manual, saya tidak yakin apakah Anda menyadarinya, tetapi setiap kali dependensi MusicDatabase dibutuhkan, kami membuat instance yang berbeda.
Ini tidak ideal karena kami mungkin ingin menggunakan kembali instance MusicDatabase yang sama di seluruh aplikasi. Sebagai ganti fungsi, kita bisa berbagi instance yang sama dengan menyimpannya semua dalam sebuah variabel.
Pada dasarnya, kami memasukkan tipe MusicDatabase ke container ini karena kami selalu menyediakan instance yang sama sebagai dependensi. Bagaimana cara melakukannya dengan Hilt? Yah, tidak ada kejutan di sini... Dengan anotasi yang lain!
Dengan anotasi @Singleton dalam metode @Provides, kita memberi tahu Hilt untuk selalu membagikan instance yang sama dari tipe ini dalam komponen tersebut.
@Singleton adalah anotasi cakupan. Dan setiap komponen Hilt memiliki anotasi cakupan yang terkait.
Jika Anda ingin cakupan tipe ke ActivityComponent, Anda harus menggunakan anotasi ActivityScoped. Anotasi ini bisa digunakan di Modul, tetapi juga dapat menganotasikan class yang konstruktornya dianotasikan dengan @Inject.
Ada dua tipe binding:
Hilt menawarkan integrasi dengan library Jetpack terpopuler: ViewModel, Navigation, Compose, dan WorkManager.
Selain ViewModel, setiap integrasi memerlukan library yang berbeda untuk ditambahkan ke proyek Anda. Lihat dokumentasi untuk informasi selengkapnya tentang hal ini. Apakah Anda ingat kode FeedViewModel dari iosched yang kita lihat di awal postingan blog ini? Ingin melihat tampilannya dengan dukungan Hilt?
Selain menganotasikan konstruktor dengan @Inject, untuk memberi tahu Hilt cara menyediakan instance ViewModel ini, kita perlu menganotasikan class dengan @HiltViewModel.
Itu saja! Anda tidak perlu secara manual membuat penyedia ViewModel untuk hal ini, Hilt akan menanganinya.
Hilt dibangun di atas library injeksi dependensi populer lainnya: Dagger! Dagger akan sering disebut dalam episode berikutnya! Jika Anda menggunakan Dagger, Dagger dan Hilt bisa bekerja sama. Baca selengkapnya tentang API migrasi di panduan.
Untuk informasi lebih lanjut tentang Hilt, kami memiliki tips praktis dengan anotasi terpopuler, apa yang dilakukannya, dan cara menggunakannya. Selain dokumen di Hilt, kami juga memiliki codelab untuk belajar praktik langsung.
Dan itu saja untuk episode ini! Namun ini tidak berakhir di sini! Kami punya lebih banyak episode MAD skills yang akan segera hadir, jadi silakan ikuti publikasi Android Developers Medium untuk melihat kapan mereka diposting.
Good post! I hope you keep working like this in the future, so you can write more interesting geometry dash lite.
Good post! I hope you keep working like this in the future, so you can write more interesting geometry dash lite.
ReplyDelete