简体   繁体   中英

How to combine livedata and kotlin flow

Is this good to put the collect latest inside observe?

viewModel.userProfile.observe(viewLifecycleOwner){
            when(it){
                is NetworkState.Error -> Timber.e(it.error)
                is NetworkState.Loading -> {}
                is NetworkState.Success -> {
                    viewLifecycleOwner.lifecycleScope.launch {
                        viewModel.getCommunityFeed(it.data?.groups?.get(0)?.slug).collectLatest { communityFeedPageData->
                            binding.swipeRefreshFeed.isRefreshing = false
                            communityFeedAdapter.submitData(communityFeedPageData)
                        }
                    }
                }
            }

OR

viewModel.fetchUserProfileLocal(PreferencesManager(requireContext()).userName!!)
            .observe(viewLifecycleOwner) {
                if (!it.groups.isNullOrEmpty()) {
        viewLifecycleOwner.lifecycleScope.launch {
            viewModel.referralDetailsResponse.collect { referralResponseState ->
                when (referralResponseState) {
                    State.Empty -> {
                    }
                    is State.Failed -> {}
                    State.Loading -> {}
                    is State.Success<*> -> {
                        if (it.groups.isEmpty())
                            // ACCESS LIVEDATA OBSERVABLE RESULT LIKE THIS???
                    }
                }
            }
        }

I'm sure it isn't, my API is called thrice too as the local DB changes, what is the right way to do this?

it certainly is not a good practice to put a collect inside the observe.

I think what you should do is collect your livedata/flows inside your viewmodel and expose the 'state' of your UI from it with different values (either Flows or Livedata)

for example in your first code block I would change it like this

  • get rid of "userProfile" from your viewmodel
  • create and expose from your viewmodel to your activity three LiveData/StateFlow objects for your communityFeedPageData, errorMessage, refreshingState
  • then in your viewmodel, where you would update the "userProfile" update the three new LiveDatas

this way you will take the business logic of "what to do in each state" outside from your activity and inside your viewmodel, and your Activity's job will become to only update your UI based on values from your viewmodel

For the specific case of your errorMessage and because you want to show it only once and not re-show it on Activity rotation, consider exposing a hot flow like this:

private val errorMessageChannel = Channel<CharSequence>()
val errorMessageFlow = errorMessageChannel.receiveAsFlow()

What "receiveAsFlow()" does really nicely is that something emitted to the channel will be collected by one collector only, so a new collector (eg if your activity recreates on a rotation) will not gether the message and your user will not see it again

You can combine both streams of data into one stream and use their results. For example we can convert LiveData to Flow , using LiveData.asFlow() extension function, and combine both flows:

combine(
  viewModel.fetchUserProfileLocal(PreferencesManager(requireContext()).userName!!),
  viewModel.referralDetailsResponse
) { userProfile, referralResponseState  ->
  ...
}.launchIn(viewLifecycleOwner.lifecycleScope)

But it is better to move combining logic to ViewModel class and observe the overall result.

Dependency to use LiveData.asFlow() extension function:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM