cren90
cren90

Reputation: 1427

Kotlin MutableStateFlow.collect is dropping values

I have an android app in which I'm trying to use coroutine flows to replace the existing Otto EventBus using my own event bus library. I'm seeing dropped values when setting the MutableStateFlow's value and then collecting in my app's code. I've created a much simpler project that demonstrates the same issue. Any thoughts on why values are being dropped from the MutableStateFlow?

Project's build.gradle

buildscript {
    ext.kotlin_version = "1.4.10"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.3'
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
        classpath 'com.google.firebase:perf-plugin:1.3.1'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

App Module's build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.firebase.firebase-perf'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.cren90.sandbox"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.fragment:fragment:1.2.5'
    implementation 'androidx.fragment:fragment-ktx:1.2.5'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    implementation 'com.google.firebase:firebase-analytics:17.5.0'
    implementation 'com.google.firebase:firebase-crashlytics:17.2.1'
    implementation 'com.google.firebase:firebase-perf:19.0.8'
    implementation 'com.google.firebase:firebase-messaging:20.2.4'
    implementation 'com.jakewharton.timber:timber:4.7.1'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.navigation:navigation-runtime:2.3.0'
    implementation 'androidx.navigation:navigation-fragment:2.3.0'
    implementation 'androidx.navigation:navigation-ui:2.3.0'
    implementation 'androidx.navigation:navigation-runtime-ktx:2.3.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'

    testImplementation 'junit:junit:4.13'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

MainActivity.kt

package com.cren90.sandbox

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.navigation.findNavController
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import timber.log.Timber
import java.lang.RuntimeException

class MainActivity : AppCompatActivity() {

    val flow: MutableStateFlow<Int> = MutableStateFlow<Int>(0)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)



        CoroutineScope(Dispatchers.IO).launch {
            flow.collect {
                Timber.d("cren90 - $it")
            }
        }
    }

    override fun onStart() {
        super.onStart()
        (application as SandboxApplication).apply {
            startupTrace.putAttribute("APP_UUID", uuid)
            startupTrace.stop()
        }

    }

    override fun onResume() {
        super.onResume()


        for(i in 0..100) {
            flow.value = i
        }
    }
}

I'd expect this to log

2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 0
2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 1
2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 2
...
2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 98
2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 99
2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 100

but instead it logs

2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 0
//Maybe 1 or 2 other random values in the 0-100 range
2020-09-30 18:10:02.826 10533-10611/com.cren90.sandbox D/MainActivity$onCreate$1$invokeSuspend$$inlined$collect: cren90 - 100

Upvotes: 9

Views: 8551

Answers (1)

Glenn Sandoval
Glenn Sandoval

Reputation: 3745

StateFlow official documentation states:

The value of mutable state flow can be updated by setting its value property. Updates to the value are always conflated. So a slow collector skips fast updates, but always collects the most recently emitted value.

You can find StateFlow official documentation here.

Upvotes: 18

Related Questions