Dasar-dasar Dagger

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:

  1. Cara membuat instance UserRepository dengan konstruktor @Inject beranotasi.

  2. Apa saja dependensinya: UserLocalDataSource dan UserRemoteDataSource.

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:

  1. Anda ingin jenis lain yang memiliki jenis ini sebagai dependensi untuk berbagi instance yang sama, seperti beberapa objek ViewModel dalam alur login menggunakan LoginUserData yang sama.

  2. 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.