问题
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
回答1:
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.
来源:https://stackoverflow.com/questions/64147848/kotlin-mutablestateflow-collect-is-dropping-values