Long Ranger
Long Ranger

Reputation: 6008

Kotlin Flow : How can i get the cache data from subscription in flow when i have new subscriber?

In my use case I have a fragment with a data subscription, which fetch data per 3 seconds automatically. And when the fragment is destroyed, this subscription will be stopped because no subscriber using this data subscription.

However, if I opened a popup dialog fragment from this fragment and the dialog fragment is connected with data subscription. I will get a weird user experience that user might wait up to 3 seconds to see the data. I found out that the first fragment is not on paused and it will treat the first call flag as invalid and delay 3 seconds to emit the new data.

case:

Fragment A -> Dialog Fragment B

Here is my repository class with a temporary solution using simple flag and while loop. I am using flow, how could i send the last result to the new observer so they dun have to wait up to 3 seconds to receive new data?

Appreciate any advice or comments

DataRepository.kt

class DataRepository {
    private val isCalledFirstTime = AtomicBoolean(true)
    private val latestData by lazy {
            flow {
                while (true) {
                    if (hasSubscription()) {
                        try {
                            val response = restService.getData()
                            emit(response)
                            isCalledFirstTime.set(false)
                        } catch (e: Exception) {
                            logger.e(e)
                        }
                    }
                    if (isCalledFirstTime.get()) {
                        delay(200)
                    } else {
                        var count = 0
                        do {
                            delay(200)
                        } while (count++ < (200 / 3000) && !isCalledFirstTime.get())
                    }
                }
            }
    }
    fun observeData() = latestData
}

Upvotes: 3

Views: 5161

Answers (2)

Wilson Tran
Wilson Tran

Reputation: 4811

Because your latestData is a cold flow you have to convert it to a hot flow like StateFlow or SharedFlow to get the cached value with the same publisher.

To make that, we will use Flow.shareIn operator

class DataRepository() {
    private val repoScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    private val latestData by lazy {
            flow {
                // Your code
            }.shareIn(
                repoScope,
                replay = 1,
                started = SharingStarted.WhileSubscribed()
            )
    }
}
  • replay: How many values do you want to get from cached when starting subscribed.

More information here : Making cold flows hot using shareIn

Upvotes: 1

R&#243;bert Nagy
R&#243;bert Nagy

Reputation: 7642

I think your LiveData might become inactive, when your view gets into PAUSED state.

I'd double check if this is the case, with LiveData.onInactive() Also I wouldn't convert it to LiveData I'd go with observing simply the flow:

repository.observeData().flowWithLifecycle(this, Lifecycle.State.STARTED)
    .onEach {
        ...
    }
    .launchIn(lifecycleScope) // or viewLifecycleScope

Upvotes: 0

Related Questions