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是不合适的,就监听个状态还能把主线程累死吗。

相关推荐
alexhilton1 小时前
Android XR 开发入门完全指南
android·kotlin·android jetpack
我命由我123451 天前
匈牙利命名法
java·服务器·后端·学习·java-ee·kotlin·学习方法
1 天前
Android 自定义 View 实战:从零打造工业级全向摇杆控件(OmniJoystickView)
android·kotlin·自定义view·摇杆控件
雨白1 天前
深入理解 Kotlin 协程 (十一):以逸待劳,探秘 select 多路复用与并发安全策略
android·kotlin
刘名喜1 天前
第22篇-数据库迁移-Liquibase
kotlin·springboot
刘名喜1 天前
第30篇-Spring-Security-7核心概念
后端·kotlin·springboot
我命由我123452 天前
Jetpack Compose - Material Design 断点范围、WindowSizeClass、针对不同屏幕尺寸创建预览、四类导航栏
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
刘名喜2 天前
第24篇-全局异常处理
kotlin·springboot
我命由我123452 天前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
深海呐2 天前
仓颉语言是ArkTs的上层语言吗?就像kotlin和Java
java·开发语言·kotlin·仓颉