Giới thiệu
Trong phần trước, chúng ta đã cùng nhau thảo luận về việc không nên khởi tạo trạng thái trong khối init {}
của ViewModel. Trong bài viết này, chúng ta sẽ tiếp tục khám phá các điểm quan trọng trong việc sử dụng ViewModel một cách hiệu quả, tập trung vào hai vấn đề chính: tránh để lộ các mutable states và sử dụng update {}
khi làm việc với MutableStateFlow
.
1. Các điểm chính trong loạt bài viết về ViewModels
Dưới đây là danh sách tổng hợp những điều cần lưu ý khi làm việc với ViewModel:
- Tránh khởi tạo trạng thái trong khối
init {}
. - Tránh để lộ các mutable states.
- Sử dụng
update {}
khi sử dụngMutableStateFlows
: - Lazy inject dependencies trong constructor.
- Sử dụng code reactive hơn và ít ràng buộc hơn.
- Tránh khởi tạo ViewModel từ bên ngoài.
- Tránh truyền tham số từ bên ngoài.
- Tránh hardcode Coroutine Dispatchers.
- Unit test ViewModels.
- Tránh để lộ suspended functions.
- Tận dụng
onCleared()
callback trong ViewModels. - Xử lý sự cố của quá trình và thay đổi cấu hình.
- Inject các UseCase trong Repositories, sau đó gọi các DataSource.
- Chỉ bao gồm domain objects trong ViewModels.
- Tận dụng các toán tử
shareIn()
vàstateIn()
để giảm số lần upstream.
2. Điều Nên Tránh: Để Lộ Các Mutable States ❌
Việc để lộ MutableStateFlow
trực tiếp từ ViewModel có thể dẫn đến nhiều vấn đề nghiêm trọng liên quan đến kiến trúc ứng dụng, tính toàn vẹn dữ liệu và bảo trì mã nguồn. Dưới đây là một số vấn đề chính:
Vi phạm tính đóng gói
Tính đóng gói là nguyên tắc cơ bản trong lập trình hướng đối tượng. Khi mutable states được hiển thị công khai, các lớp bên ngoài có thể sửa đổi trạng thái trực tiếp, dẫn đến hành vi không mong muốn và vi phạm trách nhiệm của ViewModel trong việc quản lý trạng thái.
Rủi ro về tính toàn vẹn dữ liệu
Khi trạng thái bị thay đổi từ bên ngoài mà không có kiểm soát, việc duy trì tính toàn vẹn của dữ liệu trở nên khó khăn. Điều này có thể dẫn đến các trạng thái không hợp lệ và làm tăng nguy cơ lỗi trong ứng dụng.
Tăng độ phức tạp
Cho phép biến trạng thái bị sửa đổi từ các phần khác nhau trong mã nguồn làm cho việc theo dõi và quản lý chúng trở nên phức tạp hơn. Điều này có thể gây khó khăn cho việc debug và bảo trì mã nguồn.
Các vấn đề về concurrency
Nếu nhiều phần của ứng dụng có thể cập nhật trạng thái đồng thời mà không đồng bộ, có thể xảy ra tình trạng tương tranh, dẫn đến hành vi không ổn định.
Khó khăn trong việc kiểm tra
Khi mutable states có thể sửa đổi từ bên ngoài, việc kiểm tra các thay đổi trạng thái trở nên phức tạp và không đáng tin cậy.
Kiến trúc không rõ ràng
Việc để lộ trạng thái mutable gây bối rối trong kiến trúc ứng dụng, khó phân định vai trò của các lớp khác nhau.
Thiếu kiểm soát đối với người quan sát
Khi trạng thái có thể thay đổi bên ngoài, việc kiểm soát thông báo về các thay đổi trạng thái trở nên khó khăn hơn, dẫn đến cập nhật giao diện người dùng không cần thiết hoặc thất lạc.
Ví dụ về việc để lộ mutable state không hợp lý
kotlin
class RatesViewModel constructor(
private val ratesRepository: RatesRepository,
) : ViewModel() {
val state = MutableStateFlow(RatesUiState(isLoading = true))
}
Cách Giải Quyết: Không Để Lộ Mutable State 🤔
Để tránh những vấn đề trên, bạn cần cung cấp trạng thái từ ViewModel dưới dạng chỉ đọc, chẳng hạn như StateFlow
hoặc LiveData
. Điều này không chỉ duy trì tính đóng gói mà còn cho phép ViewModel kiểm soát trạng thái hiệu quả hơn. Thay vì để lộ MutableStateFlow
, bạn có thể sử dụng một biến private cho state mà chỉ có ViewModel mới có thể cập nhật và cung cấp quyền truy cập dạng chỉ đọc cho các lớp bên ngoài.
Ví dụ Cách Làm Đúng
kotlin
class RatesViewModel constructor(
private val ratesRepository: RatesRepository,
) : ViewModel() {
private val _state = MutableStateFlow(RatesUiState(isLoading = true))
val state: StateFlow<RatesUiState>
get() = _state.asStateFlow()
}
3. Sử Dụng update {}
Khi Làm Việc Với MutableStateFlows
📜
Khi sử dụng MutableStateFlow
, việc cập nhật giá trị cần phải thực hiện đúng cách để gần như luôn đảm bảo tính an toàn và chính xác. Có ba phương pháp để cập nhật trạng thái:
Cách 1: Gán Trực Tiếp
kotlin
mutableStateFlow.value = mutableStateFlow.value.copy()
Cách này đơn giản nhưng không đảm bảo tính atomic.
Cách 2: Emit Trạng Thái Mới
kotlin
mutableStateFlow.emit(newState())
.Dùng .emit()
có thể được gọi trong coroutine nhưng đây là một suspended function.
Cách 3: Sử Dụng .update {}
kotlin
mutableStateFlow.update { it.copy(// sửa đổi trạng thái tại đây) }
Sử dụng .update {}
là cách làm tốt nhất vì:
- Tính atomic: Mỗi bản cập nhật sẽ chắc chắn được thực hiện dựa trên trạng thái mới nhất.
- An toàn luồng: Quá trình này tự động quản lý an toàn đa luồng.
- Đơn giản quả lý trạng thái: Không cần quản lý coroutine phức tạp.
Ví dụ cụ thể
Giả sử bạn có MutableStateFlow
cho trạng thái người dùng:
kotlin
data class User(val name: String, val age: Int)
val userStateFlow = MutableStateFlow(User(name = "John", age = 30))
userStateFlow.update { currentUser ->
currentUser.copy(age = currentUser.age + 1)
}
Kết Luận
Chúng ta đã nêu bật những kỹ thuật quan trọng trong việc phát triển ứng dụng với ViewModel trong Android. Bằng cách tránh những lỗi phổ biến và áp dụng các phương pháp tốt, bạn có thể làm cho ứng dụng của mình trở nên an toàn hơn, dễ kiểm tra hơn và dễ duy trì hơn. Hãy nhớ áp dụng những mẹo này để xây dựng ứng dụng Android tốt hơn và hiệu quả hơn.
Cảm ơn bạn đã theo dõi bài viết này!
Nguồn: ProAndroidDev
source: viblo