#android #kotlin #mvvm
#Android #kotlin #mvvm
Вопрос:
Я работаю с RecyclerView и использую Retrofit для извлечения данных с сервера. Я использую Kotlin с шаблоном проектирования MVVM. Я использовал LiveData, он работал нормально. Но с потоком состояний возникают проблемы, когда мы переходим к другому фрагменту и снова возвращаемся к тому же фрагменту. Он просто снова извлекает те же данные. Ниже приведен код для ViewModel и наблюдателя:
// Просмотр модели
private val _allTimeSheetsResponse =
MutableStateFlow<ResponsesResult<AllTimeSheetsResponse>>(ResponsesResult.Empty)
val allTimeSheetsResponse : StateFlow<ResponsesResult<AllTimeSheetsResponse>> get() = _allTimeSheetsResponse
fun getAllTimeSheets(auth: String) =
viewModelScope.launch {
timeSheetsRepository.getAllTimeSheets(auth).collect {
_allTimeSheetsResponse.value = it
}
}
// Наблюдатель
lifecycleScope.launchWhenStarted{
timeSheetsViewModel.allTimeSheetsResponse.collect { timeSheetsResponse ->
when (timeSheetsResponse) {
is ResponsesResult.Loading -> binding.progressBarLayout.show()
is ResponsesResult.Failure -> {
binding.progressBarLayout.gone()
binding.nothingFoundLayout.show()
handleApiError(timeSheetsResponse)
}
is ResponsesResult.Success -> {
binding.progressBarLayout.gone()
if (timeSheetsResponse.value.payload.isNotEmpty()) {
showAllTimeSheetsRecyclerAdapter.submitList(timeSheetsResponse.value.payload)
} else {
binding.nothingFoundLayout.show()
}
}
else -> Unit
}
}
}
Ответ №1:
Потому что вы вызываете getAllTimeSheets
много раз (например, onCreateView or
onViewCreated ). Trying call it when accessing
allTimeSheetsResponse` в первый раз.
Ответ №2:
Ваша getAllTimeSheets()
функция ViewModel запускает новую сопрограмму для сбора данных из холодного потока репозитория каждый раз, когда вы ее вызываете, поэтому каждый раз, когда фрагмент возвращается, предположительно. Вы должны удалить эту функцию и просто преобразовать холодный поток репозитория непосредственно в поток состояний:
val allTimeSheetsResponse: StateFlow<ResponsesResult<AllTimeSheetsResponse>> =
timeSheetsRepository.getAllTimeSheets(auth)
.stateIn(viewModelScope, SharingStarted.Eagerly, ResponsesResult.Empty)
Вы можете передать auth
параметр на фабрику ViewModel через его конструктор.
Комментарии:
1. Как я должен вызывать функцию приостановки без области сопрограмм ? Более конкретно, как я должен использовать предоставленную вами строку кода? Должен ли я заменить весь код ViewModels кодом, который вы предоставили, или его нужно прикрепить к существующему коду?
2. Где в моем коде вызывается функция приостановки? Это просто объявление свойства.
Ответ №3:
Когда вы используете navigationComponent и вызываете navController.navigate()
для открытия фрагмента, в фоновом режиме фрагмент назначения заменяется фрагментом старого назначения. таким образом, старый фрагмент будет храниться в фоновом режиме FragmentManager. но его представление будет уничтожено. и при обратном переходе старый фрагмент поступает из backStack (не создается повторно) и создается только его представление.
Поэтому лучше вызывать getAllTimeSheets()
фрагменты onCreate
. (для вызова один раз). Когда выборка будет завершена, все данные будут установлены в _allTimeSheetsResponse
И затем вы должны наблюдать allTimeSheetsResponse
в onViewCreated
with viewLifecycleOwner
scope .
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.allTimeSheetsResponse.onEach { response ->
// do sth with response
}.launchIn(viewLifecycleOwner.lifecycleScope)
}
Ответ №4:
fun getAllTimeSheets(auth: String) :StateFlow<ResponsesResult<AllTimeSheetsResponse>> {
var mutableStateFlow = MutableStateFlow<ResponsesResult<AllTimeSheetsResponse>>(ResponsesResult.Empty)
viewModelScope.launch {
timeSheetsRepository.getAllTimeSheets(auth).collect {
mutableStateFlow.value = it
}
}
return mutableStateFlow
}