kotlin flow onEach not being triggered

Viewed 1836

I'm trying to store value using DataStore.


class BasicDataStore(context: Context) :
    PrefsDataStore(
        context,
        PREF_FILE_BASIC
    ),
    BasicImpl {

    override val serviceRunning: Flow<Boolean>
        get() = dataStore.data.map { preferences ->
            preferences[SERVICE_RUNNING_KEY] ?: false
        }

    override suspend fun setServiceRunningToStore(serviceRunning: Boolean) {
        dataStore.edit { preferences ->
            preferences[SERVICE_RUNNING_KEY] = serviceRunning
        }
    }

    companion object {
        private const val PREF_FILE_BASIC = "basic_preference"
        private val SERVICE_RUNNING_KEY = booleanPreferencesKey("service_running")
    }
}

@Singleton
interface BasicImpl {
    val serviceRunning: Flow<Boolean>
    suspend fun setServiceRunningToStore(serviceRunning: Boolean)
}

And in a Service, trying to monitor that value, here is the respective code :

private fun monitorNotificationService() {
        Log.d("d--mua-entry-service","entry")
        CoroutineScope(Dispatchers.IO).launch {
            Log.d("d--mua-entry-service","entry scope")
            basicDataStore.serviceRunning.collect{
                Log.d("d--mua-entry-service","$it current status - collect")
            }
            basicDataStore.serviceRunning.onEach {
                Log.d("d--mua-entry-service","$it current status - on each")
            }
        }
    }

In EntryService :

    init {
        basicDataStore = BasicDataStore(this)
    }

But it seems that onEach isn't working at all. And collect is working once, as it should. So how should I monitor/observe a flow?

Logcat :

2021-03-25 20:41:49.462 30761-30761/com.mua.roti D/d--mua-entry-service: entry
2021-03-25 20:41:49.465 30761-30900/com.mua.roti D/d--mua-entry-service: entry scope
2021-03-25 20:41:49.471 30761-30901/com.mua.roti D/d--mua-entry-service: false current status - collect
1 Answers

I haven't used DataStore, but I expect the Flow is infinite, meaning it never finishes collecting until you cancel the coroutine. So any code below the first call to collect() will never be reached. Also, when you call onEach, it simply returns another Flow. The code in onEach won't be called until you collect that returned Flow. onEach is for tacking on side effects to the collecting you will be doing later. Or alternatively for setting up what to do when using launchIn to collect it.

It is a code smell to be creating a CoroutineScope that you don't store in a property for cancellation. The point of the scope is that you can cancel it when the lifetime of the associated component is over. If you create it and throw away your reference like this, you can never cancel its children, so calls to collect, for example, will leak the surrounding class. If you are working on Android, you rarely if ever need to create any CoroutineScope because the framework provides scopes for various lifecycle components like Activity, Fragment, ViewModel, and LifecycleService.

Related