Compose下StateFlow误用导致输入跳变

问题复线

一段只有一个输入框的demo代码:

kotlin 复制代码
class SomeVM {
    val textState = MutableStateFlow("")

    fun update(text: String) {
        textState.value = text
    }
}

@Composable
@Preview
fun App() {
    MaterialTheme {
        val vm = remember(::SomeVM)
        val text by vm.textState.collectAsState()

        TextField(text, onValueChange = vm::update)
    }
}

运行后,连续快速按1、2两个按键,数次后发生输入错乱。

问题排查

上面示例使用了StateFlow,首先去掉StateFlow,换成Compose内置的State,即

kotlin 复制代码
class SomeVM {
    val textState = mutableStateOf("")

    fun update(text: String) {
        textState.value = text
    }
}

@Composable
@Preview
fun App() {
    MaterialTheme {
        val vm = remember(::SomeVM)
        val text by vm.textState

        TextField(text, onValueChange = vm::update)
    }
}

问题消失。原生的State没问题,StateFlow被大量使用有问题的概率也不大,因此看将StateFlow转换为State的方法:

原来collectAsState方法的参数context有默认值 EmptyCoroutineContext ,而其对应的 Dispatchers.Default 就是在异步线程在执行了。

所以问题就很明了了,用户输入和其他UI变化在主线程,而StateFlow的监听却到后台线程倒了一下手,主线程取到错误的文本长度导致焦点位置错误就是正常的了。

解决方案

解决的方法就是主动传入下Dispatchers.Main保证状态监听也在主线程就可以了。

kotlin 复制代码
class SomeVM {
    val textState = MutableStateFlow("")

    fun update(text: String) {
        textState.value = text
    }
}

@Composable
@Preview
fun App() {
    MaterialTheme {
        val vm = remember(::SomeVM)
        val text by vm.textState.collectAsState(Dispatchers.Main.immediate)

        TextField(text, onValueChange = vm::update)
    }
}

当然还要注意,Compose在JVM平台使用的Swing框架,需要添加依赖协程库才能知道哪个线程是主线程。

kotlin 复制代码
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.8.0")

更多相关

为何在VM中要把状态定义成StateFlow,直接用Compose的State不就啥事情都没有了?

定义成StateFlow可以方便地对数据进行一些操作,且保证VM中不包含任何UI代码,便于测试。

而且上面例子也是比较特殊的,大多数场景,点击XX按钮,不会像输入框这样有这么高的实时性要求。另外我个人认为,这里默认值用EmptyCoroutineContext是不合适的,就监听个状态还能把主线程累死吗。

相关推荐
萌面小侠Plus1 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
wk灬丨5 小时前
Android Kotlin Flow 冷流 热流
android·kotlin·flow
晨曦_子画5 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
大福是小强5 小时前
005-Kotlin界面开发之程序猿初试Composable
kotlin·界面开发·桌面应用·compose·jetpack·可组合
&岁月不待人&8 小时前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
小白学大数据10 小时前
正则表达式在Kotlin中的应用:提取图片链接
开发语言·python·selenium·正则表达式·kotlin
bytebeats2 天前
Kotlin 注解全面指北
android·java·kotlin
jzlhll1232 天前
kotlin android Handler removeCallbacks runnable不生效的一种可能
android·开发语言·kotlin
&岁月不待人&2 天前
Kotlin 协程使用及其详解
开发语言·kotlin
苏柘_level62 天前
【Kotlin】 基础语法笔记
开发语言·笔记·kotlin