1

This answer show us how can we use liveData in the repository to return a LiveData to ViewModel which the View will observe.
Now, what if we want to use SavedStateHandle? How can we transform the following code to use liveData{} from 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)
    }
}

Activity:

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

Will we benefit from using liveData{... emit()}?

I can see 3 advantages of using SavedStateHandle:
1 - Restore your viewModel state
2 - It calls .value() for us when we use stateHandle.set()
3 - stateHandle.getLiveData helps us initialize the value (this is also helpful with Data Binding for example)

GuilhE
  • 11,591
  • 16
  • 75
  • 116
  • means you want to use stateHandle in activity right? – Shweta Chauhan Sep 24 '19 at 11:37
  • No, i'm using inside `ViewModel` https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle and `stateHandle.set` calls `mutableLiveData.setValue(value);` for me which is fine. I wan't to know who to merge this with `liveData{}` or if there's no benefit in it while using `SavedStateHandle` – GuilhE Sep 24 '19 at 13:26

2 Answers2

1

Actually, with savedStateHandle, the get/set methods feel like a mistake to me. The only one that is truly reliable is getLiveData, which you can combine into other LiveData using Transformations.switchMap.

If you use getLiveData with a type that is supported by android.os.Bundle, then you get state persistence out of the box through it. If you don't, then you'll just get crashes. getLiveData already returns a MutableLiveData, which is why it is not a good idea to handle get/set manually, you can vall .value = on the MutableLiveData from SavedStateHandle if need be.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • Actually changing the `SavedStateHandle` by `.value=` makes sense, but why does `get/set` feel like a mistake, can you elaborate? Thanks. – GuilhE Sep 26 '19 at 00:53
  • 1
    I would think the idea of `handle.get()` is for values that don't change over time (for example values from the arguments bundle passed in as default args on handle init), but as it has a `set()` it clearly can change over time? But if it can change, then you want to be able to observe said changes, in which case you wanted a LiveData in the first place. So not sure where you'd ever want to use `handle.set()` instead of `handle.getLiveData()`. – EpicPandaForce Sep 26 '19 at 08:30
  • I've replace this type of code: `stateHandle.set(KEY, value)` with `_MyLiveData.value = value` and it works just fine as expected. But when I was using `.set` the value overtime would change correctly because inside `set` it calls the `.value` for me. That said, wont `.value` or `.set` end up doing the same? – GuilhE Sep 26 '19 at 10:06
0

I think you can do something like this

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)
    }
}

Let me know if it helped you. P.S. PageId it can be number of current page or other any page identifier

  • Thanks for your response. What's the difference from what I have? Maybe I misinterpreted your answer. Where do you use https://developer.android.com/topic/libraries/architecture/coroutines#livedata ? – GuilhE Sep 24 '19 at 15:33
  • it uses in repository call. I update my answer @GuilhE – Alexander Kazantsev Sep 24 '19 at 15:40
  • I was betting on that but now I'm sure :) Thanks for your response! Your code makes sense but I can't test it right now. Did you test it? – GuilhE Sep 24 '19 at 16:44
  • No, i did`t test it code but I use same construction in own applications. And sametimes I append UseCase layer. – Alexander Kazantsev Sep 24 '19 at 18:26
  • How to perform `SwitchMap` on itemLiveData? – IgorGanapolsky May 08 '20 at 18:45
  • @AlexanderKazantsev: There is a bug in your answer: You cannot save state in onCleared(), the system will just ignore anything you save there. Have a look here https://stackoverflow.com/questions/62800028/how-to-lazily-save-viewmodels-savedstatehandle – Wess Oct 22 '20 at 06:11