liveData с сопрограммами и SavedStateHandle

Этот ответ показывает нам, как мы можем использовать liveData в репозиторий, чтобы вернуть LiveData в ViewModel, который будет наблюдать View.
А что, если мы хотим использовать SavedStateHandle? Как мы можем преобразовать следующий код для использования liveData{} из LiveData-Ktx ?

ViewModel:

private val _itemLiveData = stateHandle.getLiveData<MyItem>(KEY, MyItem())
val itemLiveData: LiveData<MyItem> = _itemLiveData

suspend fun nextPage() {
    viewModelScope.launch {
        val item = repoCall...
        stateHandle.set(KEY, item)
    }
}

Деятельность:

viewModel.itemLiveData.observe(this, Observer {
    lifecycleScope.launch {/*...update ui...*/}
})

Получим ли мы пользу от использования liveData{... emit()}?

Я вижу 3 преимущества использования SavedStateHandle:
1 - Восстановление состояния вашей модели viewModel;
2 - Он вызывает у нас .value(), когда мы используем stateHandle.set().
3 - stateHandle.getLiveData помогает нам инициализировать значение (это также полезно с Data Binding Например)


comment
означает, что вы хотите использовать stateHandle в действии, верно?   -  person Shweta Chauhan    schedule 24.09.2019
comment
Нет, я использую внутри ViewModel developer.android.com/reference/androidx/lifecycle/ и stateHandle.set звонит мне mutableLiveData.setValue(value);, и это нормально. Я не хочу знать, с кем объединить это с liveData{} или нет ли в этом пользы при использовании SavedStateHandle   -  person GuilhE    schedule 24.09.2019


Ответы (2)


Я думаю ты можешь сделать что-то подобное

class SomeViewModel(
    private val savedStateHandle: SavedStateHandle
    repository:ItemsRepository) : ViewModel() {

    companion object {
        private const val PAGE_KEY = "page_key"
    }

    private val _page = MutableLiveData<PageId>(savedStateHandle.get(PAGE_KEY))

    private val _itemLiveData = Transformations.switchMap(_page) { pageId -> repository.getNextPage(pageId) }
    val itemLiveData: LiveData<MyItem> = _itemLiveData

    suspend fun nextPage(pageId: PageId) {
        _page.postValue(pageId)
    }

    override fun onCleared() {
        super.onCleared()
        savedStateHandle.set(PAGE_KEY, _page.value)
    }
}


class ItemsRespository {

    fun getNextPage(pageId:PageId) = liveData() {
        ....
        emit(someData)
    }
}

Сообщите мне, помогло ли это вам. P.S. PageId это может быть номер текущей страницы или любой другой идентификатор страницы.

person Alexander Kazantsev    schedule 24.09.2019

На самом деле, с savedStateHandle методы get/set кажутся мне ошибкой. Единственный действительно надежный - это getLiveData, который вы можете объединить с другими LiveData, используя Transformations.switchMap.

Если вы используете getLiveData с типом, который поддерживается android.os.Bundle, вы получаете постоянство состояния прямо из коробки. Если вы этого не сделаете, вы просто получите сбои. getLiveData уже возвращает MutableLiveData, поэтому не рекомендуется обрабатывать get/set вручную. При необходимости вы можете vall .value = на MutableLiveData из SavedStateHandle.

person EpicPandaForce    schedule 25.09.2019
comment
На самом деле изменение SavedStateHandle на .value= имеет смысл, но почему get/set кажется ошибкой, не могли бы вы уточнить? Спасибо. - person GuilhE; 26.09.2019
comment
Я бы подумал, что идея handle.get() предназначена для значений, которые не меняются с течением времени (например, значения из пакета аргументов, переданного в качестве аргументов по умолчанию при инициализации дескриптора), но поскольку у него есть set(), он явно может измениться со временем? Но если он может измениться, тогда вы хотите иметь возможность наблюдать за этими изменениями, и в этом случае вам в первую очередь нужны LiveData. Так что не уверен, где вы когда-нибудь захотите использовать handle.set() вместо handle.getLiveData(). - person EpicPandaForce; 26.09.2019
comment
Я заменил этот тип кода: stateHandle.set(KEY, value) на _MyLiveData.value = value, и он работает нормально, как и ожидалось. Но когда я использовал .set, значение сверхурочной работы изменилось бы правильно, потому что внутри set он вызывает для меня .value. Тем не менее, разве .value или .set не сделают то же самое? - person GuilhE; 26.09.2019