Injeksi dependensi manual atau pencari layanan di aplikasi Android dapat menjadi masalah, bergantung pada ukuran project Anda. Anda dapat membatasi kompleksitas project sembari meningkatkan skalanya dengan menggunakan Dagger untuk mengelola dependensi.
Secara otomatis Dagger akan membuat kode yang meniru kode yang seharusnya akan Anda tulis secara manual. Karena kode dihasilkan pada waktu kompilasi, kode tersebut dapat dilacak dan berperforma lebih baik daripada solusi berbasis refleksi lainnya seperti Panduan.
Manfaat menggunakan Dagger
Dagger membebaskan Anda dari penulisan kode boilerplate yang melelahkan serta rawan error dengan:
Menghasilkan kode
AppContainer
(grafik aplikasi) yang Anda implementasikan secara manual di bagian DI manual.Membuat factory untuk class yang tersedia dalam grafik aplikasi. Inilah cara dependensi terpenuhi secara internal.
Menentukan apakah akan menggunakan kembali dependensi atau membuat instance baru melalui penggunaan cakupan.
Membuat container untuk alur tertentu seperti yang Anda lakukan dengan alur login di bagian sebelumnya menggunakan subkomponen Dagger. Hal ini akan meningkatkan performa aplikasi Anda dengan merilis objek dalam memori ketika objek tersebut tidak diperlukan lagi.
Secara otomatis Dagger melakukan semua operasi ini pada waktu build selama Anda mendeklarasikan dependensi class dan menentukan cara untuk memenuhinya menggunakan anotasi. Dagger menghasilkan kode yang mirip dengan yang akan Anda tulis secara manual. Secara internal, Dagger membuat grafik objek yang dapat dirujuk untuk menemukan cara dalam menyediakan instance class. Untuk setiap class dalam grafik, Dagger menghasilkan class jenis factory yang digunakannya secara internal untuk mendapatkan instance dari jenis tersebut.
Pada waktu build, Dagger menelusuri kode Anda dan:
Membuat dan memvalidasi grafik dependensi, memastikan bahwa:
- Setiap dependensi objek dapat dipenuhi, sehingga tidak ada pengecualian runtime.
- Tidak ada siklus dependensi, sehingga tidak ada loop tak terbatas.
Menghasilkan class yang digunakan pada waktu proses untuk membuat objek aktual dan dependensinya.
Kasus penggunaan sederhana di Dagger: Membuat factory
Untuk mendemonstrasikan cara bekerja dengan Dagger, buatlah factory sederhana
untuk class UserRepository
yang ditampilkan dalam
diagram berikut:
Tentukan UserRepository
seperti berikut:
Kotlin
class UserRepository( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
Java
public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } ... }
Tambahkan anotasi @Inject
ke konstruktor UserRepository
sehingga Dagger mengetahui
cara membuat UserRepository
:
Kotlin
// @Inject lets Dagger know how to create instances of this object class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
Java
public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; // @Inject lets Dagger know how to create instances of this object @Inject public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } }
Dalam cuplikan kode di atas, Anda memberi tahu Dagger:
Cara membuat instance
UserRepository
dengan konstruktor@Inject
beranotasi.Apa saja dependensinya:
UserLocalDataSource
danUserRemoteDataSource
.
Sekarang Dagger mengetahui cara membuat instance UserRepository
, tetapi tidak
tahu cara membuat dependensinya. Jika Anda juga menganotasi class lain,
Dagger mengetahui cara membuatnya:
Kotlin
// @Inject lets Dagger know how to create instances of these objects class UserLocalDataSource @Inject constructor() { ... } class UserRemoteDataSource @Inject constructor() { ... }
Java
public class UserLocalDataSource { @Inject public UserLocalDataSource() { } } public class UserRemoteDataSource { @Inject public UserRemoteDataSource() { } }
Komponen Dagger
Dagger dapat membuat grafik dependensi dalam project Anda yang dapat
digunakan untuk mengetahui tempat mendapatkan dependensi tersebut saat diperlukan.
Agar Dagger melakukan ini, Anda harus membuat antarmuka dan menganotasikannya dengan
@Component
. Dagger membuat container seperti yang akan Anda lakukan dengan
injeksi dependensi manual.
Dalam antarmuka @Component
, Anda dapat menentukan fungsi yang menampilkan
instance class yang Anda perlukan (misalnya UserRepository
). @Component
memberi tahu
Dagger untuk membuat container dengan semua dependensi yang diperlukan untuk memenuhi
jenis yang ditampilkan. Ini disebut komponen Dagger; komponen ini berisi
grafik yang terdiri dari objek yang diketahui Dagger tentang cara
penyediaan dan masing-masing dependensinya.
Kotlin
// @Component makes Dagger create a graph of dependencies @Component interface ApplicationGraph { // The return type of functions inside the component interface is // what can be provided from the container fun repository(): UserRepository }
Java
// @Component makes Dagger create a graph of dependencies @Component public interface ApplicationGraph { // The return type of functions inside the component interface is // what can be consumed from the graph UserRepository userRepository(); }
Saat Anda membuat project, Dagger menghasilkan implementasi
antarmuka ApplicationGraph
untuk Anda: DaggerApplicationGraph
. Dengan
pemroses anotasi, Dagger membuat grafik dependensi yang terdiri dari
hubungan antara tiga class (UserRepository
,
UserLocalDatasource
, dan UserRemoteDataSource
) dengan hanya satu titik masuk:
mendapatkan instance UserRepository
. Anda dapat menggunakannya seperti berikut:
Kotlin
// Create an instance of the application graph val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() // Grab an instance of UserRepository from the application graph val userRepository: UserRepository = applicationGraph.repository()
Java
// Create an instance of the application graph ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); // Grab an instance of UserRepository from the application graph UserRepository userRepository = applicationGraph.userRepository();
Dagger membuat instance UserRepository
baru setiap kali diminta.
Kotlin
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() val userRepository: UserRepository = applicationGraph.repository() val userRepository2: UserRepository = applicationGraph.repository() assert(userRepository != userRepository2)
Java
ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); UserRepository userRepository = applicationGraph.userRepository(); UserRepository userRepository2 = applicationGraph.userRepository(); assert(userRepository != userRepository2)
Terkadang, Anda harus memiliki instance unik dari dependensi dalam container. Anda mungkin menginginkannya karena beberapa alasan:
Anda ingin jenis lain yang memiliki jenis ini sebagai dependensi untuk berbagi instance yang sama, seperti beberapa objek
ViewModel
dalam alur login menggunakanLoginUserData
yang sama.Pembuatan objek berharga mahal, dan Anda tidak ingin membuat instance baru setiap kali objek tersebut dideklarasikan sebagai dependensi (misalnya, parser JSON).
Dalam contoh ini, Anda mungkin ingin memiliki instance UserRepository
unik yang tersedia dalam grafik sehingga setiap kali Anda meminta UserRepository
, Anda
akan selalu mendapatkan instance yang sama. Ini berguna dalam contoh Anda karena dalam
aplikasi sungguhan dengan grafik aplikasi yang lebih kompleks, Anda mungkin memiliki
beberapa objek ViewModel
, bergantung pada UserRepository
dan Anda tidak ingin
membuat instance UserLocalDataSource
dan UserRemoteDataSource
baru
setiap kali UserRepository
perlu disediakan.
Dalam injeksi dependensi manual, Anda melakukannya dengan meneruskan instance UserRepository
yang sama ke konstruktor class ViewModel; tetapi
di Dagger, karena tidak menulis kode tersebut secara manual, Anda harus memberi tahu
Dagger bahwa Anda ingin menggunakan instance yang sama. Hal ini dapat dilakukan dengan anotasi
cakupan.
Pencakupan dengan Dagger
Anda dapat menggunakan anotasi cakupan untuk membatasi masa aktif suatu objek hingga masa aktif komponennya. Ini berarti bahwa instance dependensi yang sama digunakan setiap kali jenis tersebut perlu diberikan.
Untuk memiliki instance UserRepository
unik saat Anda meminta repositori
di ApplicationGraph
, gunakan anotasi cakupan yang sama untuk antarmuka
@Component
dan UserRepository
. Anda dapat menggunakan anotasi @Singleton
yang
sudah disertakan dalam paket javax.inject
yang digunakan oleh Dagger:
Kotlin
// Scope annotations on a @Component interface informs Dagger that classes annotated // with this annotation (i.e. @Singleton) are bound to the life of the graph and so // the same instance of that type is provided every time the type is requested. @Singleton @Component interface ApplicationGraph { fun repository(): UserRepository } // Scope this class to a component using @Singleton scope (i.e. ApplicationGraph) @Singleton class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource ) { ... }
Java
// Scope annotations on a @Component interface informs Dagger that classes annotated // with this annotation (i.e. @Singleton) are scoped to the graph and the same // instance of that type is provided every time the type is requested. @Singleton @Component public interface ApplicationGraph { UserRepository userRepository(); } // Scope this class to a component using @Singleton scope (i.e. ApplicationGraph) @Singleton public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; @Inject public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } }
Atau, Anda dapat membuat dan menggunakan anotasi cakupan kustom. Anda dapat membuat anotasi cakupan seperti berikut:
Kotlin
// Creates MyCustomScope @Scope @MustBeDocumented @Retention(value = AnnotationRetention.RUNTIME) annotation class MyCustomScope
Java
// Creates MyCustomScope @Scope @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomScope {}
Kemudian, Anda dapat menggunakannya seperti sebelumnya:
Kotlin
@MyCustomScope @Component interface ApplicationGraph { fun repository(): UserRepository } @MyCustomScope class UserRepository @Inject constructor( private val localDataSource: UserLocalDataSource, private val service: UserService ) { ... }
Java
@MyCustomScope @Component public interface ApplicationGraph { UserRepository userRepository(); } @MyCustomScope public class UserRepository { private final UserLocalDataSource userLocalDataSource; private final UserRemoteDataSource userRemoteDataSource; @Inject public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) { this.userLocalDataSource = userLocalDataSource; this.userRemoteDataSource = userRemoteDataSource; } }
Dalam kedua kasus tersebut, objek disediakan dengan cakupan yang sama dengan yang digunakan untuk menganotasi
antarmuka @Component
. Jadi, setiap kali memanggil
applicationGraph.repository()
, Anda mendapatkan instance
UserRepository
yang sama.
Kotlin
val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create() val userRepository: UserRepository = applicationGraph.repository() val userRepository2: UserRepository = applicationGraph.repository() assert(userRepository == userRepository2)
Java
ApplicationGraph applicationGraph = DaggerApplicationGraph.create(); UserRepository userRepository = applicationGraph.userRepository(); UserRepository userRepository2 = applicationGraph.userRepository(); assert(userRepository == userRepository2)
Kesimpulan
Penting untuk mengetahui manfaat Dagger dan dasar-dasar cara kerjanya sebelum Anda dapat menggunakannya dalam skenario yang lebih rumit.
Di halaman berikutnya, Anda akan mempelajari cara menambahkan aplikasi Dagger ke aplikasi Android.