ViewModel
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”ViewModel là class giữ và quản lý UI-related data theo cách lifecycle-aware.
2. Đặc điểm
Phần tiêu đề “2. Đặc điểm”- Survive configuration changes (xoay màn hình)
- Có scope lớn hơn Activity/Fragment
- Tự động cleared khi owner destroyed
- Không hold reference đến View/Context
3. Tạo ViewModel cơ bản
Phần tiêu đề “3. Tạo ViewModel cơ bản”class CounterViewModel : ViewModel() {
private val _count = MutableStateFlow(0) val count: StateFlow<Int> = _count.asStateFlow()
fun increment() { _count.value++ }
fun decrement() { _count.value-- }}4. Sử dụng trong Activity/Fragment
Phần tiêu đề “4. Sử dụng trong Activity/Fragment”class MainActivity : ComponentActivity() {
// Lazy initialization private val viewModel: CounterViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)
setContent { val count by viewModel.count.collectAsState()
Counter( count = count, onIncrement = viewModel::increment, onDecrement = viewModel::decrement ) } }}5. ViewModel với parameters
Phần tiêu đề “5. ViewModel với parameters”ViewModelFactory
Phần tiêu đề “ViewModelFactory”class UserViewModel( private val userId: Int, private val repository: UserRepository) : ViewModel() { // ...}
class UserViewModelFactory( private val userId: Int, private val repository: UserRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T { return UserViewModel(userId, repository) as T }}
// Usageval viewModel: UserViewModel by viewModels { UserViewModelFactory(userId, repository)}Với Hilt (Recommended)
Phần tiêu đề “Với Hilt (Recommended)”@HiltViewModelclass UserViewModel @Inject constructor( private val repository: UserRepository, savedStateHandle: SavedStateHandle) : ViewModel() {
private val userId: Int = savedStateHandle.get<Int>("userId") ?: 0}
// Usage (no factory needed)val viewModel: UserViewModel = hiltViewModel()6. SavedStateHandle
Phần tiêu đề “6. SavedStateHandle”Lưu state survive process death:
class SearchViewModel( private val savedStateHandle: SavedStateHandle) : ViewModel() {
// Tự động save/restore var searchQuery: String get() = savedStateHandle.get<String>("query") ?: "" set(value) = savedStateHandle.set("query", value)
// Hoặc dùng StateFlow val searchQueryFlow: StateFlow<String> = savedStateHandle.getStateFlow("query", "")}7. Shared ViewModel
Phần tiêu đề “7. Shared ViewModel”Chia sẻ data giữa Fragments:
// SharedViewModelclass SharedViewModel : ViewModel() { private val _selectedItem = MutableStateFlow<Item?>(null) val selectedItem: StateFlow<Item?> = _selectedItem.asStateFlow()
fun selectItem(item: Item) { _selectedItem.value = item }}
// Fragment Aclass ListFragment : Fragment() { private val sharedViewModel: SharedViewModel by activityViewModels()
fun onItemClick(item: Item) { sharedViewModel.selectItem(item) }}
// Fragment Bclass DetailFragment : Fragment() { private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(...) { lifecycleScope.launch { sharedViewModel.selectedItem.collect { item -> displayItem(item) } } }}8. Compose với ViewModel
Phần tiêu đề “8. Compose với ViewModel”@Composablefun UserScreen( viewModel: UserViewModel = viewModel()) { val uiState by viewModel.uiState.collectAsState()
when (val state = uiState) { is UiState.Loading -> LoadingScreen() is UiState.Success -> UserContent(state.user) is UiState.Error -> ErrorScreen(state.message) }}9. ViewModel Lifecycle
Phần tiêu đề “9. ViewModel Lifecycle”flowchart TD A["📱 Activity/Fragment Created"] --> B["🆕 ViewModel Created<br/>(nếu chưa tồn tại)"] B --> C["🔄 Configuration Change<br/>(rotation)"] C --> D["♻️ Activity/Fragment<br/>Destroyed & Recreated"] D --> E["✅ ViewModel STILL EXISTS<br/>(same instance)"] E --> F["🚪 Activity finished OR<br/>Fragment detached permanently"] F --> G["🧹 ViewModel.onCleared()"] G --> H["❌ ViewModel Destroyed"]
style B fill:#e8f5e9,stroke:#4caf50 style E fill:#e3f2fd,stroke:#2196f3 style G fill:#fff3e0,stroke:#ff9800 style H fill:#ffebee,stroke:#f4433610. onCleared
Phần tiêu đề “10. onCleared”Cleanup resources khi ViewModel destroyed:
class MyViewModel : ViewModel() {
private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.Main + job)
override fun onCleared() { super.onCleared() job.cancel() // Cancel all coroutines // Cleanup other resources }}Note: Với
viewModelScope, không cần manual cleanup vì nó tự cancel.
11. Best Practices
Phần tiêu đề “11. Best Practices”// ✅ Good: Expose immutable stateprivate val _uiState = MutableStateFlow(UiState())val uiState: StateFlow<UiState> = _uiState.asStateFlow()
// ✅ Good: Use viewModelScopeviewModelScope.launch { val data = repository.getData()}
// ❌ Bad: Hold Context/View referenceclass BadViewModel(private val context: Context) : ViewModel()
// ❌ Bad: Expose MutableStateFlowval uiState = MutableStateFlow(UiState()) // Can be modified from outside
// ❌ Bad: Business logic in ViewModel// Move to Repository or UseCase📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Feature | Description |
|---|---|
| Lifecycle | Survives config changes |
| Scope | viewModelScope |
| State | StateFlow / LiveData |
| SavedState | SavedStateHandle |
| Sharing | activityViewModels() |
| Cleanup | onCleared() |