Код-ревью Senior ViewModel и сохранение состояния обновлено
Код-ревью ViewModel профиля
Вопрос
Проведи ревью ViewModel экрана профиля. Найди ошибки и места, которые стоит улучшить.
class ProfileViewModel(
private val activity: ProfileActivity,
private val repo: ProfileRepository,
) : ViewModel() {
val state = MutableStateFlow<ProfileUiState>(ProfileUiState.Loading)
var draftName: String = ""
fun load(userId: String) {
GlobalScope.launch(Dispatchers.Main) {
try {
val profile = repo.loadProfile(userId)
state.value = ProfileUiState.Content(profile)
} catch (e: Exception) {
state.value = ProfileUiState.Error(e.message ?: "")
activity.showToast("Ошибка")
}
}
}
fun onNameChanged(name: String) {
draftName = name
state.value = (state.value as ProfileUiState.Content).copy(name = name)
}
}
Коротко
Главные проблемы: утечка Activity через конструктор и GlobalScope вместо viewModelScope. Показ тоста должен быть событием или частью состояния, а не вызовом Activity.
Что должен найти ревьюер
- Ошибкастрока 2Ссылка на Activity во ViewModel: после поворота ViewModel держит уничтоженную Activity, утечка памяти и тост на мёртвом экране
- Стильстрока 5Публичный MutableStateFlow: UI может менять состояние в обход ViewModel; нужен приватный _state и публичный StateFlow
- Ошибкастрока 6Черновик имени в обычном поле теряется при смерти процесса; хранить в SavedStateHandle
- Ошибкастрока 9GlobalScope: запрос не отменится при закрытии экрана; нужен viewModelScope
- Ошибкастрока 13catch (e: Exception) глотает CancellationException, отменённая корутина продолжит работу
- Ошибкастрока 22Небезопасное приведение as Content: если состояние Loading или Error, ClassCastException; использовать update и проверку типа
Разбор
Главные проблемы: утечка Activity через конструктор и GlobalScope вместо viewModelScope. Показ тоста должен быть событием или частью состояния, а не вызовом Activity. Состояние наружу отдаётся только для чтения. Ввод пользователя, который должен пережить смерть процесса, хранится в SavedStateHandle. Обработка ошибок не должна перехватывать отмену, а изменение состояния лучше делать через _state.update { } с проверкой текущего варианта.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.