Skip to Content

ViewModel trong Android

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

  • 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

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

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

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 } } // Usage val viewModel: UserViewModel by viewModels { UserViewModelFactory(userId, repository) }
@HiltViewModel class 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

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

Chia sẻ data giữa Fragments:

// SharedViewModel class SharedViewModel : ViewModel() { private val _selectedItem = MutableStateFlow<Item?>(null) val selectedItem: StateFlow<Item?> = _selectedItem.asStateFlow() fun selectItem(item: Item) { _selectedItem.value = item } } // Fragment A class ListFragment : Fragment() { private val sharedViewModel: SharedViewModel by activityViewModels() fun onItemClick(item: Item) { sharedViewModel.selectItem(item) } } // Fragment B class DetailFragment : Fragment() { private val sharedViewModel: SharedViewModel by activityViewModels() override fun onViewCreated(...) { lifecycleScope.launch { sharedViewModel.selectedItem.collect { item -> displayItem(item) } } } }

8. Compose với ViewModel

@Composable fun 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

Activity/Fragment Created ViewModel Created (nếu chưa tồn tại) Configuration Change (rotation) Activity/Fragment Destroyed & Recreated ViewModel STILL EXISTS (same instance) Activity finished OR Fragment detached permanently ViewModel.onCleared() called ViewModel Destroyed

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

// ✅ Good: Expose immutable state private val _uiState = MutableStateFlow(UiState()) val uiState: StateFlow<UiState> = _uiState.asStateFlow() // ✅ Good: Use viewModelScope viewModelScope.launch { val data = repository.getData() } // ❌ Bad: Hold Context/View reference class BadViewModel(private val context: Context) : ViewModel() // ❌ Bad: Expose MutableStateFlow val uiState = MutableStateFlow(UiState()) // Can be modified from outside // ❌ Bad: Business logic in ViewModel // Move to Repository or UseCase

📝 Tóm tắt

FeatureDescription
LifecycleSurvives config changes
ScopeviewModelScope
StateStateFlow / LiveData
SavedStateSavedStateHandle
SharingactivityViewModels()
CleanuponCleared()
Last updated on