Спасибо за этот вопрос! Это то, над чем я работал в последнее время. Несколько замечаний по этому поводу:
В следующие дни мы опубликуем sh модуль интеграции KotlinX для Arrow. Я позволю вам охватить ваши задачи ввода-вывода в CoroutineScope. Т.е.:
yourIOTask().unsafeRunScoped(scope) { cb -> }
Это обеспечит отмену ваших задач ввода-вывода в случае отмены предоставленной области. Это означает, что вы могли бы охватить ваши операции «модели представления», такие как doNetworkRequest
в этом примере, используя область действия модели представления, и вы будете уверены, что они выдержат изменение конфигурации и будут отменены, когда модель представления будет выпущена.
Сказал, что, и если мы посмотрим, как Android ViewModels работает в данный момент, вам все еще понадобится "кэш среднего уровня", чтобы доставлять результаты, как вы упоминаете, чтобы гарантировать, что эти результаты всегда доставляются и как только вид начинает наблюдаться, я получаю самые свободные sh возможные данные. Имея этот механизм вместе с областью, упомянутой в предыдущем параграфе, вы можете гарантировать, что ваши долгосрочные задачи всегда будут приносить результаты, независимо от того, были ли они выполнены до, во время или после изменения конфигурации.
В в этом смысле, и если вы хотите продолжать использовать Android ViewModel под капотом, вы можете закодировать что-нибудь, используя стрелку, например:
interface ViewStateCache<ViewState> {
val cacheScope: CoroutineScope
fun observeViewState(observer: LifecycleOwner, renderingScope: CoroutineScope, render: (ViewState) -> IO<Unit>): IO<Unit>
fun updateViewState(transform: (ViewState) -> ViewState): IO<ViewState>
}
Мы могли бы использовать этот контракт, чтобы убедиться, что ViewModels используются в чистом виде , Все ViewModels могут реализовать этот контракт, например:
class ViewModelViewStateCache<ViewState>(initialState: ViewState) : ViewModel(), ViewStateCache<ViewState> {
override val cacheScope: CoroutineScope = viewModelScope
private val _viewState = MutableLiveData<ViewState>(initialState)
private val viewState: LiveData<ViewState> = _viewState
override fun updateViewState(transform: (ViewState) -> ViewState) =
IO {
val transformedState = transform(viewState.value!!)
_viewState.postValue(transformedState)
transformedState
}
override fun observeViewState(observer: LifecycleOwner, renderingScope: CoroutineScope, render: (ViewState) -> IO<Unit>) =
IO {
viewState.observe(observer, Observer<ViewState> { viewState ->
viewState?.let { render(it).unsafeRunScoped(renderingScope) {} }
})
}
}
. Таким образом, вы фактически получаете кэш состояния представления, который реализуется с использованием Android ViewModel. Это деталь реализации, так что вы можете добавить ее. Ваша программа будет работать с интерфейсом.
Здесь ViewModel работает только как кеш для доставки результатов в , и она сделана чистой, оборачивая свои операции для наблюдения и обновления состояния просмотра в IO
.
Используя что-то подобное, вы могли бы иметь чистые функции, которые кодируют вашу логику представления и координации потоков и доставляют результаты в упомянутый кеш, например:
fun doNetworkRequest(): IO<Unit> = IO.fx {
!viewStateCache.updateViewState { Loading }
!repository.requestSomeResult().redeemWith(
ft = {
viewStateCache.updateViewState { ErrorViewState(ServerError) }
},
fe = { error ->
viewStateCache.updateViewState { ErrorViewState(error) }
},
fb = { data ->
viewStateCache.updateViewState { SuccessfulViewState(data) }
}
)
}
Эти функции не нужно было бы жить в модели представления, но вместо этого использовать кеш в качестве делегата для доставки результатов, поэтому он может быть внедрен как деталь реализации .
Вам также понадобится начать наблюдение за кэшем состояния представления, как только представление будет создано, так что это будет похоже на то, что вы уже делали с вашими моделями представления. Обратите внимание, что мы намеренно представили область действия как часть контракта на кэш, чтобы вы могли иметь к ней доступ извне.
Это пример того, как можно обернуть текущий API ViewModel, чтобы продолжать использовать его и поддержка их изменений конфигурации, в основном для постепенного перехода на Arrow.
Этот подход больше похож на удобный подход и может потребовать некоторой доработки, но должен работать. В настоящее время мы находимся в процессе изучения Android обычных проблем, таких как изменения конфигурации, чтобы обеспечить беспрепятственный доступ для тех, кто через расширения или подобное, в библиотеку интеграции.
Надеюсь, что это было достаточно полезно, если нет, дайте мне знать ?