Giới thiệu về Dagger 2,cách sử dụng Dependency Injection in Android (Phần 2)

Chào các bạn, đây là bài thứ 2 trong loạt bài viết hướng dẫn về Dependency Injection và sử dụng thư viện Dagger 2 trong Android. Nếu bạn chưa đọc qua phần 1, bạn có thể đọc ở đây.

Phần này mình sẽ xây dựng 1 ứng dụng demo để mô tả cách thức làm việc với Dagger 2 trong Android. 1 ứng dụng hết sức cơ bản thôi để giúp các bạn hình dung được cách làm. Đây là video demo của ứng dụng:

Đây là ứng dụng có chức năng load dữ liệu về các Hotgirl từ trong database SQLite ra và hiển thị ra RecyclerView. Nó còn có thêm chức năng load 1 đoạn accessToken từ SharePreference và hiển thị lên Toolbar của ứng dụng. Just demo, đơn giản đúng không?

Source của ứng dụng: https://github.com/nanashi1111/DaggerExample

Bắt đầu

Đầu tiên, bạn hãy nhìn vào cấu trúc của dự án.

Các thành phần cốt lõi của ứng dụng bao gồm:

DataManager: Lớp cung cấp các phương thức truy cập vào dữ liệu trong ứng dụng. Dữ liệu có thể lấy từ SQLite Database hoặc SharedPreference.

DbHelper: Lớp cung cấp các phương thức truy cập vào SQLite Database, lớp này được sử dụng bởi DataManager.

SharedPrefsHelper: Lớp cung cấp các phương thức làm việc với SharedPreference. Lớp này cũng được sử dụng bởi DataManager.

Hotgirl: Lớp model của ứng dụng, để lưu trữ thông tin của các hotgirl <3

Bước 1: thêm thư viện Dagger 2 vào dự án.

Thêm các câu lệnh sau vào file build.gradle (ở app module) để import thư viện Dagger 2.

Bước 2: Xây dựng class model Hotgirl

Bao giờ cũng vậy, ta cần xây dựng class Model để lưu trữ dữ liệu và làm việc với các thành phần khác của ứng dụng như database, activity…

Bước 3: Tạo ra các custom annotation

Ta sẽ tạo ra các annotation sau: ActivityContextApplicationContextDatabaseInfo , PerActivity

Tại sao phải tạo ra các annotation này?

Annotation @Qualifier nằm trong package javax.inject. Nó được sử dụng để phân biệt các đối tượng mà Dagger sẽ phân phối cho các lớp Dependency consumer. Ví dụ: 1 class có thể yêu cầu cung cấp cả ApplicationContextActivityContext. Trong khi 2 đối tượng này đều là thể hiện của lớp Context. Vậy ta cần 1 cái gì đó để giúp Dagger 2 phân biệt được 2 đối tượng này. Thứ ta cần chính là các @Qualifier annotation. Nó giúp Dagger 2 phân biệt được các đối tượng thuộc cùng 1 kiểu dữ liệu (trong trường hợp này là Context).

Annotation @DatabseInfo để cung cấp thông tin về các thuộc tính để khởi tạo DBHelper. Các thuộc tính này là name và version.

Annotation @Scope để chỉ ra vùng tồn tại của các đối tượng được dagger cung cấp. Khi 1 class được Inject các dependency bởi Dagger, và các dependency đó được chỉ định @Scope, thì mỗi thể hiện của class đó sẽ được cung cấp các dependency khác nhau, độc lập và tồn tại trong vòng đời của class đó.

Bước 4, tạo DBHelper

Tạo ra 1 class DBHelper, class này sẽ đảm nhiệm mọi công việc liên quan đến database SQLite, thêm, xoá dữ liệu, clear db,…

Hãy chú ý vào các Annotation xuất hiện trong class này:

@Singleton đảm bảo cho đối tượng DBHelper được khởi tạo duy nhất trong vòng đời của ứng dụng. Tức là cho dù ta có @Inject DBHelper ở nhiều activity, thì các đối tượng đó đều là 1.

@Inject trước hàm khởi tạo DBHelper. Chỉ ra rằng lớp DBHelper sẽ được thêm vào Dependency Graph, có nghĩa là khi cần khởi tạo đối tượng DBHelper, Dagger sẽ tìm đến phương thức khởi tạo được gắn Annotation @Inject này.

Qualifier @ApplicationContext chỉ ra rằng đối tượng context cần để khởi tạo DBHelper là đối tượng ApplicationContext, (chứ không phải ActivityContext).

@DatabaseInfo chỉ ra các thông tin cần để khởi tạo DBHelper: ở đây là name và version.

Mình sẽ quay trở lại giải thích kỹ hơn các Annotation này khi làm việc với @Module.

Tất cả các phương thức còn lại chỉ liên quan đến các operation thêm sửa xoá dữ liệu. Mình sẽ không nói kỹ thêm ở đây.

Bước 5, tạo class SharedPrefsHelper

Lớp này để xử lý các operation liên quan đến SharedPreference.

Cũng như lớp DBHelper, lớp này cũng được gắn thêm Annotation @Singleton để đảm bảo đối tượng của class này được khởi tạo 1 lần duy nhất trong vòng đời ứng dụng.

Annotation @Inject ở hàm khởi tạo cho phép thêm lớp này vào Dependency Graph và Dagger có thể sử dụng hàm khởi tạo này khi cần.

Vậy các lớp DBHelper, SharedPrefsHelper này được cung cấp như thế nào? Hãy cùng mình tìm hiểu.

Bước 6, DataManager

Phần quan trọng bắt đầu ở đây nhé. Lớp DataManager này sử dụng đối tượng của các lớp Context, DBHelper, SharedPrefsHelper. Hàm khởi tạo của lớp DataManager này cũng được gán Annotation @Inject, tức là nó cũng được gán vào Dependency Graph.

Khi Dagger cần khởi tạo DataManager, nó sẽ tìm trong Dependency Graph các class cần có trong hàm khởi tạo của lớp này. Ở đây là Context, SharePrefsHelper, và DBHelperSharePrefsHelper, và DBHelper đã được thêm vào graph thông qua Annotation @Inject nên Dagger sẽ tự động gọi hàm khởi tạo của 2 lớp này ra. Vậy còn @ApplicationContext Context context thì sao? Hãy cùng mình đi vào bước 7.

Bước 7, Application.

Ta viết 1 lớp App kế thừa Application để biểu diễn vòng đời của toàn ứng dụng như sau. Đây cũng chính là lớp để Dagger sử dụng cho việc cung cấp @ApplicationContext Context cho DataManager.

Tiếp theo thêm class này vào file Manifest:

Lưu ý: lớp App này sử dụng DataManager thông qua Annotation @Inject. Phần thú vị tiếp theo nằm ở class ApplicationComponent. Ta sẽ tìm hiểu ở các bước sau.

Bước 8, Xây dựng module cung cấp Dependency (Provider)

Đây là bước rất quan trọng, ta sẽ xây dựng các module cung cấp các Dependency để sử dụng trong activity của ta. Đầu tiên là ApplicationModule.

Như các bạn thấy module này chỉ là 1 class bình thường, ngoại trừ việc nó được gán Annotation @Module, và các phương thức của nó đều được gán Annotation @Provides. Mình sẽ giải thích vì sao lại viết các phương thức này.

Như mình đã viết ở bước 6, mình đã thêm class DataManager vào Dependency Graph. Tức là khi cần khởi tạo đối tượng DataManager, Dagger 2 sẽ tìm các thành phần cần thiết trong Dependency Graph để thực hiện hàm khởi tạo DataManager:

Ở đây Dagger sẽ tìm cách khởi tạo DBHelperSharedPrefsHelper.

Tiếp tục, để khởi tạo DBHelper, ta cần @ApplicationContext, @DatabaseInfo String, và @DatabaseInfo Integer:

Chính vì vậy, trong lớp ApplicationModule, mình đã thêm 3 phương thức sau:

3 phương thức này để cho Dagger sử dụng trong việc khởi tạo DBHelper .

Tiếp đến là khởi tạo SharedPrefsHelper. Ta cần đối tượng SharedPreference:

Vì vậy nên mình lại thêm 1 phương thức @Provides đối tượng SharedPreference:

Vậy là xong. mình đã có đầy đủ các “nguyên liệu” để giúp Dagger tạo ra đối tượng DataManager. Tiếp theo sẽ là việc viết ra ApplicationComponent để làm cầu nối giữa Dependency Provider (Module mà ta vừa viết) với Dependency Consumer (các Activity).

Bước 9, ApplicationComponent

Rất đơn giản đúng không, đây chỉ là 1 interface được gắn 2 annotation:

@Singleton: chỉ ra rằng component này sẽ tồn tại trong suốt vòng đời ứng dụng, và chỉ có 1 khởi tạo duy nhất.

@Component: chỉ ra module mà nó sẽ đảm nhiệm việc kết nối.

Phương thức inject (App app) chính là phương thức kết nối giữa Dependency Provider và Dependency Consumer (trong trường hợp này ,consumer là lớp App).

Lưu ý: Bạn có thể đặt tên phương thức inject này tuỳ ý, không nhất thiết phải là inject.

Quay lại lớp App mà ta viết ở bước 7. Hãy để ý hàm này:

Và câu lệnh khai báo:

Hàm initApplicationComponent chính là hàm đảm nhiệm việc khởi tạo ApplicationComponent và inject (kết nối ApplicationModule với App). Lưu ý rằng bạn không thể khởi tạo ApplicationComponent 1 cách trực tiếp qua câu lệnh new, mà phải thông qua builder của Dagger.

DaggerApplicationComponent là lớp mà Dagger 2 tự sinh ra cho chúng ta để khởi tạo ApplicationComponent. Đối với các Component khác cũng tương tự, ta chỉ cần build project, sau đó thêm tiền tố Dagger vào trước tên Component là có thể sử dụng builder để tạo ra đối tương Component.

Sau khi inject xong thì ta có thể thoải mái sử dụng đối tượng DataManager mà không cần phải tự tay khởi tạo nó.

Trên đây là cách thức hoạt động cũng như cách làm việc cơ bản với Dagger 2. Cám ơn bạn đã theo dõi bài viết.

 

 

 

Hãy chia sẻ

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *