0

Dagger2: những điều cần biết trước khi implement

Dagger2 đang là một thư viện khá quen thuộc với các bạn lập trình andoroid, nên ở bài này, tôi sẽ không nói lại các vấn đề cơ bản nữa. Mà sẽ đi thẳng vào cách mà dagger làm việc luôn.

Tìm hiểu cách tạo một class singleton

Một Singleton class chỉ tồn tại với một instance duy nhất cho toàn bộ ứng dụng. Trong Dagger2, chúng ta tạo ra một lớp singleton bằng cách chú thích nó với @Singleton annotation. Điều này hoạt động tốt khi chúng ta instantiate class sử dụng constructor injection. Nhưng không thành công khi chúng ta cung cấp một class sử dụng @provides trong một module theo các kịch bản cụ thể được mô tả dưới đây.

Sử dụng @Singleton cho một class và cung cấp nó với một new keyword trong @provides cua module.

Tạo một package sample và add class DependencySample1 trong nó:

@Singleton
public class DependencySample1 {

    private static final String TAG = "DependencySample1";

    private int value;

    public DependencySample1(int value) {
        this.value = value;
        Log.d(TAG, "DependencySample1: " + this.hashCode());
    }
}

Trong class DataManager, express phụ thuộc của nó trong constructor.

@Singleton
public class DataManager {
    ...
    @Inject
    public DataManager(@ApplicationContext Context context,
                       DbHelper dbHelper,
                       SharedPrefsHelper sharedPrefsHelper,
                       DependencySample1 dependencySample1Instance) {
        mContext = context;
        mDbHelper = dbHelper;
        mSharedPrefsHelper = sharedPrefsHelper;
        mDependencySample1Instance1 = dependencySample1Instance;
    }
    ...
}

Trong class DemoApplication, cũng express phụ thuộc của nó:

public class DemoApplication extends Application {
    ...
    @Inject
    DependencySample1 dependencySample1Instance1;
    ...
}

Trong ApplicationModule cung cấp phụ thuộc của class này:

@Module
public class ApplicationModule {
    ...
    @Provides
    DependencySample1 provideDependencySample1() {
        Log.d(TAG, "provideDependencySample1: provideDependencySample1 called");
        return new DependencySample1(3);
    }

Sau khi chạy app, chúng ta sẽ theo dõi nó: Các log chỉ ra rằng hai trường hợp của lớp DependencySample1 được tạo ra ngay cả khi lớp được chú thích với @Singleton.

01–12 08:09:25.389 D/ApplicationModule: provideDependencySample1: provideDependencySample1 called
01–12 08:09:25.390 D/DependencySample1: DependencySample1: 746992865
01–12 08:09:25.390 D/DataManager: DependencySample1: 746992865
01–12 08:09:25.390 D/ApplicationModule: provideDependencySample1: provideDependencySample1 called
01–12 08:09:25.390 D/DependencySample1: DependencySample1: 541540870
01–12 08:09:25.390 D/DemoApplication: DependencySample1: 541540870

Xác nhận của Heap Dump :

Từ khi sử dung @Singleton trong class DependencySample1, chúng ta hy vọng sẽ get cụng một object ở cả 2 nơi nhưng thực tế thì lại có tới 2 object khác nhau. Vậy, làm cách nào để giải quyết vấn đề trên.

Bây giờ chúng ta hãy sửa phương thức @Provides trong class ApplicationModule bằng việc add @Singleton trong method provide cho DependencySample1.

@Module
public class ApplicationModule {
    ...
    @Provides
    @Singleton
    DependencySample1 provideDependencySample1() {
        Log.d(TAG, "provideDependencySample1: provideDependencySample1 called");
        return new DependencySample1(3);
    }
    ...
}

Chúng ta lại tiếp tục theo dõi log. Và các log chỉ ra rằng lần này, chỉ có một instance được tạo ra và shared cho cả DataManager và DemoApplication.

01–12 08:07:41.004 D/ApplicationModule: provideDependencySample1: provideDependencySample1 called
01–12 08:07:41.006 D/DependencySample1: DependencySample1: 186196167
01–12 08:07:41.006 D/DataManager: DependencySample1: 186196167
01–12 08:07:41.006 D/DemoApplication: DependencySample1: 186196167

Xác nhận của Heap Dump:

Trong trường hợp chúng ta đang cung cấp các phụ thuộc của một class và class có thể xây dựng chính nó từ các phụ thuộc hiện trong đồ thị. Chúng ta sẽ có được một class Singleton bằng cách chú thích class với @Singleton nghĩa là chúng ta không sử dụng từ khóa mới trong method provide.

Thay đổi class DependencySample1 :

@Singleton
public class DependencySample1 {

    private static final String TAG = "DependencySample1";

    private int value;

    @Inject
    public DependencySample1(@Named(value = "DependencySample1_Integer") Integer value) {
        this.value = value;
        Log.d(TAG, "DependencySample1: " + this.hashCode());
    }
}

Ở đây chúng ta đang cung cấp các phụ thuộc của DependencySample1 qua constructor injection. @Named Được sử dụng để giúp Dagger giải quyết phụ thuộc kiểu Integer, vì vậy để tránh xung đột. Chúng ta sẽ sử dụng phụ thuộc kiểu Integer cho đơn giản.

Cũng sửa class method provide của ApplicationModule :

@Module
public class ApplicationModule {
    ...
    @Provides
    @Named(value = "DependencySample1_Integer")
    Integer provideDependencySample1Integer() {
        Log.d(TAG, "provideDependencySample1: provideDependencySample1Integer called");
        return 3;
    }
    ...
}

Hiện nay chúng ta đang cung cấp các phụ thuộc Integer mà sẽ construct DependencySample1 qua constructor injection. Như trong trường hợp trước, DependencySample1 được inject trong DemoApplication và class DataManager.

Theo dõi Log, chúng ta nhận được 1 class singleton DependencySample1.

01–12 08:34:28.022 D/ApplicationModule: provideDependencySample1: provideDependencySample1Integer called
01–12 08:34:28.022 D/DependencySample1: DependencySample1: 746992865
01–12 08:34:28.022 D/DataManager: DependencySample1: 746992865
01–12 08:34:28.022 D/DemoApplication: DependencySample1: 746992865

Điều gì xảy ra khi chúng ta đề cập đến một class trong một phương thức get trong một thành phần của class interface và không inject nó ở đâu?

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    ...

    DependencySample1 getDependencySample1();
}

Trong trường hợp này, các DependencySample1 không được khởi tạo cho đến khi nó được truy cập thông qua các phương thức thành phần của interface.

Case 1: getDependencySample1() không bao giờ được gọi. applicationComponent.getDependencySample1() sẽ không được dùng. DependencySample1 không được inject, @Inject không được dùng ở DependencySample1 trong bất kỳ class nào. Trong trường hợp này, DependencySample1 sẽ không được khởi tạo.

Case2: getDependencySample1() được gọi, applicationComponent.getDependencySample1() được dùng và DependencySample1 không được inject, @Inject không được dùng trong DependencySample1 với bất kỳ class nào. Trong trường hợp này, DependencySample1 được khởi tạo khi getDependencySample1() được gọi. Tất cả nguyên tắc của singleton chúng ta chứng kiến ở trên sẽ được áp dụng ở đây.

Trong test ở trên, chúng ta sửa class DemoApplication để truy cập vào getDependencySample1().

public class DemoApplication extends Application {

    ...
    private DependencySample1 dependencySample1;
    @Override
    public void onCreate() {
        super.onCreate();
        applicationComponent = DaggerApplicationComponent
                .builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        applicationComponent.inject(this);
        dependencySample1 = applicationComponent.getDependencySample1();
    }
    ...
}

Log sẽ chỉ ra rằng lần này DependencySample1 đã được khởi tạo.

Chúng ta đã hiểu các khía cạnh khác nhau của Dagger2 với các class singleton. Bây giờ chúng ta hãy tập trung vào Scope để hiểu cách và những gì xảy ra trong một biến scope?

Scope tạo ra các thể hiện của một class có các quy tắc tương tự như các Singleton nhưng sự khác biệt là Singleton tạo ra các trường hợp duy nhất global và Scope tạo ra các trường hợp duy nhất trong phạm vi đó.

Chúng ta sẽ tạo một scope @PerActivity để cung cấp các phụ thuộc cho từng activity.

@Module
public class ActivityModule {
    ...
    @Provides
    DependencySample1 provideDependencySample1() {
        Log.d(TAG, "provideDependencySample1: called");
        return new DependencySample1(3);
    }
}

Sửa DependencySample1 với annotate @PerActivity.

@PerActivity
public class DependencySample1 {

    private static final String TAG = "DependencySample1";

    private int value;

    public DependencySample1(int value) {
        this.value = value;
        Log.d(TAG, "DependencySample1: " + this.hashCode());
    }
}

Sửa MainActivity để inject DependencySample1.

public class MainActivity extends AppCompatActivity {
    
    ...

    @Inject
    DependencySample1 dependencySample1;

    @Inject
    DependencySample1 dependencySample2;
    
    ...
}

Log chỉ ra rằng có 2 khởi tạo của DependencySample1 nếu chúng ta dùng key new, điều tương tự với ApplicationModule.

01–12 04:35:57.697 D/ActivityModule: provideDependencySample1: called
01–12 04:35:57.699 D/DependencySample1: DependencySample1: 871533216
01–12 04:35:57.699 D/ActivityModule: provideDependencySample1: called
01–12 04:35:57.699 D/DependencySample1: DependencySample1: 474084953

Khi chúng ta add @PerActivity cho method provide thì chúng ta cũng get một khởi tạo đơn của DependencySample1. Giống như điều xảy ra với @Singleton.

@Module
public class ActivityModule {

    ...
    @Provides
    @PerActivity
    DependencySample1 provideDependencySample1() {
        Log.d(TAG, "provideDependencySample1: called");
        return new DependencySample1(3);
    }
    ...
}

Tất cả các quy định khác có giá trị như với @Singleton, chỉ là nó được kết hợp với mỗi activity. Mỗi Activity tạo ra tập mới của các phụ thuộc.

Nguồn: https://blog.mindorks.com/android-dagger2-critical-things-to-know-before-you-implement-275663aecc3e#.9epnudwms


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí