Một vài lưu ý nhanh đối với Dagger 2

Nếu bạn biết cách sử dụng Dagger 2 nhưng đôi khi quên cú pháp hoặc một số cách dùng cơ bản của nó. Thì hy vọng bài viết này sẽ là một nơi hỗ trợ bạn.

Và nếu bạn đang muốn học Dagger 2 từ đầu, thì dưới đây sẽ phù hợp hơn, nhưng trong bài viết này tôi sẽ làm cho nó rất đơn giản. Để mọi người cùng hiểu Dagger là gì.

Những thành phần cơ bản

Nào chúng ta cùng bắt đầu với các thành phần cơ bản.

@Component

Trong Dagger 2, phần quan trọng nhất là Component. Về cơ bản, nó là một đối tượng (sẽ được tạo tự động) để xử lý tất cả nội dung dependency injection của bạn.

Để định nghĩa một Component. Bạn có thể viết theo như dưới đây

@Component
interface FirstComponent

Bạn có thể tạo nó, ở bất cứ nơi nào trong code của bạn, sau đó bạn có thể gọi (bằng tiền tố với Dagger)

DaggerFirstComponent.create()

@Inject

Các thành phần không làm gì cho đến khi bạn sử dụng nó để giúp injection của bạn. Bạn cần nói cho nó biết đối tượng mục tiêu mà nó cần injection vào.

@Component
interface FirstComponent{
    fun inject(object: Target)
}

Với điều này, nó có thể inject bất kỳ lớp nào có chú thích(annotation) @Inject trong hàm tạo của nó. ví dụ.

class DependentClass @Inject constructor()

@Scope

Ngoài ra, đối với mỗi component, bạn có thể chú thích scope, ví dụ: chú thích @Singleton bên dưới :

@Singleton
@Component
interface FirstComponent{
    fun inject(object: Target)
}

Điều này cho phép mọi đối tượng phụ thuộc (dependent object injected) được chèn bởi thành phần này, có @Scope phù hợp sẽ chỉ được tạo một lần trong phạm vi (scope) đó. ví dụ.

@Singleton
class DependentClass @Inject constructor()

Để khai báo custom scope, người ta cần xác định một tên chú thích mới.

@Scope
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class CustomScope

@Module (và@Provides)

Tuy nhiên, nếu người ta muốn đóng gói tất cả các Lớp phụ thuộc liên quan (Dependents Classes) lại với nhau. Thì chúng ta có thể định nghĩa nó trong một module. Và Component có thể liên kết với module dưới dạng một injection của nó.

@Module
class FirstModule {
    @Provides
    fun getDependentDependentObject() = DependentDependentClass()

    @Singleton
    @Provides
    fun getDependentObject(
        dependentDependentObject: DependentDependentClass)
        = DependentClass(dependentDependentObject)
}

@Singleton
@Component(modules = [FirstModule::class])
interface FirstComponent {
    fun inject(object: Target)
}

Giả sử nếu bạn muốn inject một DependentObject bằng cách thủ công, Bạn có thể đặt nó làm tham số của hàm tạo (contructor) giống như class FirstModule như sau.

@Module
class FirstModule(
    val dependentDependentObject: DependentDependentClass) {

    @Singleton
    @Provides
    fun getDependentObject() 
        = DependentClass(dependentDependentObject)
}

Nếu như thế này thì bạn không thể sử dụng được các hàm như bên dưới nữa

DaggerFirstComponent.create() // Không thể dùng được nữa.

Thay vào đó bạn cần phải làm theo như bên dưới.

DaggerFirstComponent.builder().setFirstModule(
    FirstModule(dependentDependentObject)).build()

Nếu bạn muốn thứ gì đó tốt hơn nữa khi tạo Component mà không cần truy cập Module, hãy xem theo Link bên dưới Component.Builder

Một số thành phần khác

@Subcomponent

@Subcomponent có thể được xem như là một phần tử con của @Component. Để có hướng dẫn đơn giản nhưng đầy đủ về nó, bạn có thể tham khảo ở đây Subcomponent

Ở đây tôi cung cấp cách liên kết @Subcomponent với @Component

@Singleton
@Component(modules = [FirstModule::class])
interface FirstComponent {
    fun getSubcomponent(): FirstSubcomponent
}

@SubScope
@Subcomponent(modules = [FirstSubmodule::class])
interface FirstSubcomponent {
    fun inject(object: Target)
}

Với điều này, việc tạo FirstComponent vẫn có thể đơn giản

val firstComponent = DaggerFirstComponent.create()

Nhưng để truy cập vào subcomponent

val firstSubcomponent = firstComponent.getSubComponent()

Subcomponent có quyền truy cập vào biến của Component cha cũng như các Module Có điều mà các bạn cần lưu ý :

ComponentSubcomponent của cùng một Scope thì không thể được liên kết, trừ khi cả hai đều không cùng Scope

@Component dependencies

Bên cạnh việc có @SubComponent. Thì Component có thể phụ thuộc vào Component khác. Điều này có thể thực hiện như bên dưới đây :

@Singleton
@Component(modules = [FirstModule::class])
interface FirstComponent {
    fun inject(object: FirstTarget)
}


@AnotherScope
@Component(dependencies = [FirstComponent::class], 
    modules = [SecondModule::class])
interface SecondComponent {
    fun inject(object: SecondTarget)
}

Với các dòng code bên trên. Để tạo FirstComponent Nó chỉ đơn giản gọi.

val firstComponent = DaggerFirstComponent.create()

Nhưng để tạo ra SecondComponent chúng ta phải đảm bảo rằng FirstComponent phải được inject vào nó

DaggerSecondComponent.builder()
    .firstComponent(firstComponent).build()

Tham khảo sơ đồ bên trên, thì khả năng truy cập vào các phụ thuộc (dependencies) từ SubComponent thì giống với FirstComponent Subcomponent khả năng truy cập đầy đủ của parent Component (bao gồm các Module), Trong khi các phụ thuộc thành phần (component dependencies) chỉ có khả năng truy cập vào immediate dependent chứ không phải module. Giống như lưu ý của Subcomponent

Các @Component có cùng scope sẽ không thể liên với nhau. Trừ khi cả hai không nằm trong phạm vi.

Tổng kết

Hy vọng bạn đã hiểu đầy đủ tất cả những điều trên. Cảm ơn các bạn đã đọc bài viết này.