- vừa được xem lúc

Dependencies Injection trong Android - Phần 1: Hiểu khái niệm chính từ ứng dụng của bạn

0 0 107

Người đăng: blackpencil

Theo Viblo Asia

Lời giới thiệu

Chào các bạn. Khi lập trình Android, chúng ta đều có những design pattern riêng cho team mình. Một trong những kĩ thuật khá quen thuộc chúng ta sử dụng với design pattern MVVM chính là Dependencies Injection (DI). Đã có nhiều thư viện ra đời để hỗ trợ DI như Dagger, Hilt, Koin. Bản thân mình những ngày đầu đọc khá nhiều về những thư viện này nhưng vẫn thực sự đã rất khó hiểu, cho nên mình đã quay về điểm xuất phát là tìm hiểu xem DI thực ra là cái gì và chúng ta đã từng implement nó như thế nào. Một trong các bài viết đi từ khái niệm ban đầu là hướng dẫn Manual Dependencies Injection từ Android Developers. Hãy cùng mình xem thử nhé.

Dependencies Injection là như thế nào?

Mình nghĩ để hiểu được DI góp mặt trong những dự án Android của chúng ta như thế nào thì nên xem nó được vận dụng qua bài toán thực tế như sau:

  • Chúng ta có một ứng dụng có khá nhiều màn hình: Login, Register, Home, Setting... với mô hình MVVM
  • Luồng đăng nhập có LoginActivity là đầu vào, và chúng ta cần object LoginViewModel để xử lí logic cũng như xí lí UI.
  • Giả sử màn hình chúng ta cần cái checkbox "Lưu username cho lần đăng nhập sau" thì chúng ta vừa cần request tới API cũng như cần handle storage local (có thể là Room cũng có thể chỉ cần SharedPreferences). Như vậy object LoginViewModel cần 2 objects của UserLocalDataSource và của UserRemoteDataSource
  • UserRemoteDataSource làm việc với API thì lại cần object của Retrofit services
  • Để đỡ việc cho LoginViewModel thì chúng ta tạo 1 class Repository là UserRepository để lo việc xử lí với UserLocalDataSource và UserRemoteDataSource. Và LoginViewModel sẽ dùng object UserRepository này là được

Bạn hình dung ra sự phụ thuộc object này vào object khác chưa nào?

UserRepository chịu trách nhiệm làm việc với source local hoặc remote sẽ như thế này

class UserRepository( private val localDataSource: UserLocalDataSource, private val remoteDataSource: UserRemoteDataSource
) { ... } class UserLocalDataSource { ... }
class UserRemoteDataSource( private val loginService: LoginRetrofitService
) { ... }

Bây giờ cách đơn giản nhất là chúng ta khởi tạo hết trong LoginActivity xem thử nhé

class LoginActivity: Activity() { private lateinit var loginViewModel: LoginViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // tạo instance của Retrofit để gọi API bằng cú pháp rất quen thuộc val retrofit = Retrofit.Builder() .baseUrl("https://example.com") .build() .create(LoginService::class.java) // Rồi truyền instance retrofit đó cho việc khởi tạo một object UserRemoteDataSource val remoteDataSource = UserRemoteDataSource(retrofit) // Tiếp theo là khởi tạo object UserLocalDataSource val localDataSource = UserLocalDataSource() // Giờ khởi tạo instance của UserRepository với đầu vào từ những instances trên val userRepository = UserRepository(localDataSource, remoteDataSource) // Cuối cùng khởi tạo viewmodel với đầu vào là instace của UserRepository ở trên // Ví dụ này thì chưa nói tới khởi tạo viewmodel nhờ vào ViewModelFactory đâu nhé loginViewModel = LoginViewModel(userRepository) }
}

Đấy chính là chúng ta đang tạo những dependencies injection: sự phụ thuộc của instance này vào instance kia, một cách đơn giản nhất.

Nhưng mà, cách làm này, không được khuyến khích lắm, bởi vì:

  1. Chúng ta cần làm khá nhiều việc từ trên xuống dưới để khởi tạo một object viewmodel làm việc với data. Nghe chưa thuyết phục lắm nhỉ? Có bạn sẽ nghĩ là đã phụ thuộc nhau thì phải khai báo lần lượt chứ?
  2. Nếu màn hình Register cũng cần instance userRepository, để sau khi đăng kí xong chạy luồng đăng nhập, hoặc màn hình Setting để quản lí user profile, thì có phải chúng ta lại 1,2,3,4 hít thở để khai báo lại lần lượt không nào?
  3. Rồi kể cả giả sử có một chỗ quản lí giúp chúng ta đỡ các bước tạo ra đấy rồi, thì cũng sẽ khá khó để tái sử dụng object userRepository này ở màn hình khác, hay sử dụng chỉ 1 object quản lí user info cho toàn bộ app (chính là design pattern singleton)

Manual Dependencies Injection: Tạo container để quản lí các dependencies này

Để giải quyết quá nhiều bước thủ công tạo ra instance userRepository , ta hãy cùng nhau định nghĩa một class cung cấp những object phụ thuộc (ở đây chính là remoteDataSource và localDataSource) cho việc tạo instance userRepository đó:

class AppContainer { private val retrofit = Retrofit.Builder() .baseUrl("https://example.com") .build() .create(LoginService::class.java) private val remoteDataSource = UserRemoteDataSource(retrofit) private val localDataSource = UserLocalDataSource() // sẽ tạo một instance của UserRepository nếu chúng ta có một instance của AppContainer val userRepository = UserRepository(localDataSource, remoteDataSource)
}

Giờ làm sao sử dụng container này cho toàn app? Ta tự tạo một class MyApplication hỗ trợ cho việc cung cấp container rồi khai báo nó trong AndroidManifest.xml


class MyApplication : Application() { // Instance của AppContainer sẽ được sử dụng tại bất cử đâu trong app val appContainer = AppContainer()
}
<application android:name=".MyApplication" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyTheme">

Rồi sử dụng nó trong màn hình Login như sau

class LoginActivity: Activity() { private lateinit var loginViewModel: LoginViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // gọi property appContainer của object application để tạo userRepository cho việc khởi tạo instance của LoginViewModel val appContainer = (application as MyApplication).appContainer loginViewModel = LoginViewModel(appContainer.userRepository) }
}

Tạm ổn đúng không, chúng ta đã đỡ phải đi nhiều bước để khởi tạo loginViewModel rồi.

Kết luận

Mình hi vọng trên đây là một ví dụ giúp các bạn đang khúc mắc có thể hiểu rõ hơn Dependencies injection trong dự án thực tế. Các bạn sẽ thấy hoá ra chúng ta đã và đang implement DI suốt đấy thôi. Trong bài viết tới, mình sẽ giới thiệu về ViewModelFactory và tìm hiểu về quản lí scope trong Manual Dependencies injection.

Cám ơn các bạn đã đọc

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 254

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 189

- vừa được xem lúc

[Android] Hiển thị Activity trên màn hình khóa - Show Activity over lock screen

Xin chào các bạn, Hôm nay là 30 tết rồi, ngồi ngắm trời chờ đón giao thừa, trong lúc rảnh rỗi mình quyết định ngồi viết bài sau 1 thời gian vắng bóng. .

0 0 93

- vừa được xem lúc

Tìm hiểu Proguard trong Android

1. Proguard là gì . Cụ thể nó giúp ứng dụng của chúng ta:. .

0 0 82

- vừa được xem lúc

Làm ứng dụng học toán đơn giản với React Native - Phần 6

Chào các bạn một năm mới an khang thịnh vượng, dồi dào sức khỏe. Lại là mình đây Đây là link app mà các bạn đang theo dõi :3 https://play.google.com/store/apps/details?id=com.

0 0 51

- vừa được xem lúc

20 Plugin hữu ích cho Android Studio

1. CodeGlance. Plugin này sẽ nhúng một minimap vào editor cùng với thanh cuộn cũng khá là lớn. Nó sẽ giúp chúng ta xem trước bộ khung của code và cho phép điều hướng đến đoạn code mà ta mong muốn một cách nhanh chóng.

0 0 301