Bergabunglah bersama kami di ⁠#Android11: The Beta Launch Show pada tanggal 3 Juni!

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 selagi menambahkan skalanya dengan menggunakan Dagger untuk mengelola dependensi.

Dagger otomatis membuat kode yang meniru kode yang 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.

  • Menggunakan kembali dependensi atau membuat jenis instance baru yang bergantung pada cara Anda mengonfigurasi jenis tersebut menggunakan 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 dengan merilis objek dalam memori saat objek tersebut tidak diperlukan lagi.

Dagger otomatis melakukan semua operasi ini selama waktu build selama Anda mendeklarasikan dependensi class dan menentukan cara untuk memenuhi anotasi tersebut. Dagger menghasilkan kode yang mirip dengan yang akan Anda tulis secara manual. Secara internal, Dagger membuat grafik objek yang dapat dirujuk untuk menemukan cara untuk menyediakan instance class. Untuk setiap class dalam grafik, Dagger menghasilkan class factory-type yang digunakan 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 waktu proses.
    • 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 dalam Dagger: Pembuatan factory

Untuk mendemonstrasikan cara bekerja dengan Dagger, buatfactory 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 (mis UserRepository ). @Component memberi tahu Dagger untuk membuat container dengan semua dependensi yang diperlukan untuk memenuhi jenis yang ditampilkan. Ini disebut komponen Dagger; rilis ini berisi grafik yang terdiri dari objek yang dikerjakan oleh Dagger untuk mengetahui cara menyediakannya dan dependensinya masing-masing.

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) hanya dengan 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 membagikan instance yang sama, seperti beberapa objek ViewModel dalam alur login menggunakan LoginUserData yang sama.

  2. Objek tidak mudah dibuat 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 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 tidak ingin membuat instance UserLocalDataSource dan UserRemoteDataSource baru setiap kali UserRepository perlu disediakan.

Dalam injeksi dependensi manual, Anda melakukannya dengan meneruskan instance yang sama dari UserRepository 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.

Pembatasan menggunakan 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 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.