You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用@Immutable仍触发重组求助:Jetpack Compose中为何无效?

Compose中@Immutable注解无效导致组件不必要重组的问题解决

问题根源

你的代码里有两个核心问题,导致点击按钮修改x的值时,forY组件也会跟着重组:

  1. List类型的不稳定性:虽然给MyUsersList加了@Immutable注解,但它内部的l是List<String>类型——Compose会把List判定为不稳定类型,因为List是接口,可能指向可变实现(比如ArrayList),无法保证内容不会被修改。哪怕你传入的是listOf("a")创建的不可变列表,Compose也没法自动识别它的稳定性,所以会认为array参数可能变化,触发forY重组。
  2. 父重组导致子组件默认执行:当x.value变化时,父组件(Scaffold的content lambda)会触发重组,此时所有子Composable函数默认都会被重新调用,除非它们的所有参数都是稳定类型且和上一次的值完全相等。

解决方案

1. 改用稳定的不可变列表类型

使用kotlinx.collections.immutable库中的ImmutableList,这是Compose能识别的稳定类型,明确保证列表不可变,且equals判断基于内容。

先在Module级别的build.gradle里添加依赖:

implementation "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5"

然后修改数据类和列表创建代码:

@Immutable
data class MyUsersList(val l: ImmutableList<String>)

// 在remember中创建ImmutableList实例
val yi = remember {
    mutableStateOf(MyUsersList(listOf("a").toImmutableList()))
}

2. 让forY的参数满足稳定条件

修改forY的参数类型为ImmutableList<String>,确保Compose能识别参数的稳定性:

@Composable
fun forY(yy: Int, array: ImmutableList<String>) {
    Toast.makeText(GlobalContextForRessources.getAppContext(), "yy", Toast.LENGTH_SHORT).show()
    Text("int $yy")

    for (one in array) {
        Text(one)
    }
}

3. 可选:用remember缓存列表实例(无需额外依赖)

如果不想引入第三方库,可以通过remember缓存列表实例,保证每次重组时传入forY的都是同一个对象:

val stableList = remember { listOf("a") }
val yi = remember {
    mutableStateOf(MyUsersList(stableList))
}

此时哪怕List本身不稳定,但因为参数实例未变,Compose会判定参数没变化,跳过forY的重组。

修改后的完整代码示例

@Immutable
data class MyUsersList(val l: ImmutableList<String>)

class TEST : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            KdoumaScoreTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Log.d("TEST", "$innerPadding")

                    val x = remember { mutableStateOf(1) }
                    val y = remember { mutableStateOf(1) }
                    val yi = remember {
                        mutableStateOf(MyUsersList(listOf("a").toImmutableList()))
                    }

                    Box(modifier = Modifier.padding(innerPadding)) {
                        forX(xx = x.value)
                        forY(yy = y.value, array = yi.value.l)

                        Button(onClick = { x.value++ }, modifier = Modifier.padding(50.dp)) {
                            Text("hi")
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun forX(xx: Int) {
    Toast.makeText(GlobalContextForRessources.getAppContext(), "xx", Toast.LENGTH_SHORT).show()
    Text("int $xx")
}

@Composable
fun forY(yy: Int, array: ImmutableList<String>) {
    Toast.makeText(GlobalContextForRessources.getAppContext(), "yy", Toast.LENGTH_SHORT).show()
    Text("int $yy")

    for (one in array) {
        Text(one)
    }
}

这样修改后,点击按钮修改x.value时,只有forX会重组,forY因为参数未变化且都是稳定类型,会被Compose跳过重组。

内容的提问来源于stack exchange,提问作者kamal douma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:21:17