Android Compose 重组规则

重组基本概念

重组(Recomposition)是Compose更新UI的核心机制,当状态发生变化时,Compose会重新执行相关的Composable函数来更新UI。

触发重组的情况

1. State状态变化

1.1 MutableState变化

kotlin 复制代码
@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    
    // ✅ count变化时触发重组
    Text("Count: $count")
    Button(onClick = { count++ }) {
        Text("Increment")
    }
}

1.2 StateFlow/LiveData变化

kotlin 复制代码
@Composable
fun UserProfile(viewModel: UserViewModel) {
    // ✅ collectAsState()监听状态变化
    val user by viewModel.userState.collectAsState()
    
    Text("Name: ${user.name}") // user变化时重组
}

1.3 derivedStateOf变化

kotlin 复制代码
@Composable
fun FilteredList(items: List<String>, query: String) {
    // ✅ 当items或query变化时,filteredItems会重新计算
    val filteredItems by remember(items, query) {
        derivedStateOf {
            items.filter { it.contains(query, ignoreCase = true) }
        }
    }
    
    LazyColumn {
        items(filteredItems) { item ->
            Text(item)
        }
    }
}

2. 参数变化

2.1 直接参数变化

kotlin 复制代码
@Composable
fun Greeting(name: String) { // ✅ name变化时重组
    Text("Hello, $name!")
}

@Composable
fun Parent() {
    var name by remember { mutableStateOf("World") }
    
    Greeting(name = name) // name变化导致Greeting重组
}

2.2 Lambda参数变化

kotlin 复制代码
@Composable
fun Parent() {
    var count by remember { mutableStateOf(0) }
    
    // ❌ 每次重组都创建新的lambda,导致Child重组
    Child(onClick = { count++ })
    
    // ✅ 使用remember缓存lambda
    val onClick = remember { { count++ } }
    Child(onClick = onClick)
}

3. 父组件重组导致的级联重组

kotlin 复制代码
@Composable
fun Parent() {
    var parentState by remember { mutableStateOf(0) }
    
    Column {
        Text("Parent: $parentState") // 状态变化
        
        // ❌ 父组件重组时,所有子组件默认也会重组
        Child1()
        Child2()
        Child3()
    }
}

// ✅ 解决方案:使用稳定的参数或记忆化
@Composable
fun OptimizedParent() {
    var parentState by remember { mutableStateOf(0) }
    
    Column {
        Text("Parent: $parentState")
        
        // ✅ 使用remember避免重组
        remember { Child1() }
        Child2() // 如果Child2没有参数且内容稳定,可能会跳过重组
    }
}

4. 不稳定参数导致的重组

4.1 不稳定的数据类

kotlin 复制代码
// ❌ 不稳定的类
class UnstableData(var value: String)

@Composable
fun UnstableComponent(data: UnstableData) {
    // 即使data.value没变,也可能重组
    Text(data.value)
}

// ✅ 稳定的数据类
@Stable
data class StableData(val value: String)

// 或使用@Immutable
@Immutable
data class ImmutableData(val value: String)

4.2 函数类型参数

kotlin 复制代码
@Composable
fun Parent() {
    var state by remember { mutableStateOf(0) }
    
    // ❌ 每次重组创建新函数
    Child { state++ }
    
    // ✅ 缓存函数引用
    val callback = remember(state) { { state++ } }
    Child(callback)
}

5. CompositionLocal变化

kotlin 复制代码
val LocalTheme = compositionLocalOf { LightTheme }

@Composable
fun App() {
    var isDark by remember { mutableStateOf(false) }
    
    CompositionLocalProvider(
        LocalTheme provides if (isDark) DarkTheme else LightTheme
    ) {
        // ✅ LocalTheme变化时,所有使用它的组件都会重组
        ThemedContent()
    }
}

@Composable
fun ThemedContent() {
    val theme = LocalTheme.current // 监听主题变化
    Box(backgroundColor = theme.backgroundColor) {
        Text("Themed content")
    }
}

6. Side Effects触发的重组

6.1 LaunchedEffect

kotlin 复制代码
@Composable
fun TimerComponent() {
    var time by remember { mutableStateOf(0) }
    
    // ✅ 定时器更新state,触发重组
    LaunchedEffect(Unit) {
        while (true) {
            delay(1000)
            time++
        }
    }
    
    Text("Time: $time")
}

6.2 DisposableEffect

kotlin 复制代码
@Composable
fun LocationComponent() {
    var location by remember { mutableStateOf<Location?>(null) }
    
    DisposableEffect(Unit) {
        val listener = LocationListener { newLocation ->
            location = newLocation // ✅ 触发重组
        }
        
        onDispose { /* cleanup */ }
    }
    
    Text("Location: ${location?.toString() ?: "Unknown"}")
}

重组优化策略

1. 使用稳定的参数

less 复制代码
// ✅ 使用不可变数据
@Immutable
data class UserInfo(val name: String, val age: Int)

@Composable
fun UserCard(user: UserInfo) {
    // user是稳定的,只有实际内容变化才重组
}

2. 合理使用remember

kotlin 复制代码
@Composable
fun ExpensiveComponent(data: List<String>) {
    // ✅ 缓存复杂计算结果
    val processedData = remember(data) {
        data.map { it.uppercase() }.sortedBy { it.length }
    }
    
    LazyColumn {
        items(processedData) { item ->
            Text(item)
        }
    }
}

3. 状态提升和局部化

kotlin 复制代码
// ❌ 状态在顶层,导致整个组件树重组
@Composable
fun BadExample() {
    var searchQuery by remember { mutableStateOf("") }
    var isLoading by remember { mutableStateOf(false) }
    
    Column {
        SearchBar(query = searchQuery, onQueryChange = { searchQuery = it })
        LoadingIndicator(isLoading = isLoading)
        ContentList(query = searchQuery)
    }
}

// ✅ 状态局部化
@Composable
fun GoodExample() {
    Column {
        SearchSection()    // 搜索状态只影响这个区域
        LoadingSection()   // 加载状态只影响这个区域
        ContentSection()   // 内容状态只影响这个区域
    }
}

4. 使用key()优化列表

kotlin 复制代码
@Composable
fun ItemList(items: List<Item>) {
    LazyColumn {
        items(
            items = items,
            key = { item -> item.id } // ✅ 提供稳定的key
        ) { item ->
            ItemRow(item)
        }
    }
}

5. 避免在Composable中创建对象

kotlin 复制代码
@Composable
fun BadComponent() {
    // ❌ 每次重组都创建新对象
    val padding = PaddingValues(16.dp)
    val shape = RoundedCornerShape(8.dp)
    
    Box(modifier = Modifier.padding(padding))
}

@Composable
fun GoodComponent() {
    // ✅ 在外部定义或使用remember
    Box(modifier = Modifier.padding(16.dp))
}

// 或者
private val DefaultPadding = PaddingValues(16.dp)
private val DefaultShape = RoundedCornerShape(8.dp)

总结

重组触发的主要情况:

  1. State变化 - 最常见的触发原因
  2. 参数变化 - 包括直接参数和lambda参数
  3. 父组件重组 - 级联效应
  4. 不稳定参数 - 导致不必要的重组
  5. CompositionLocal变化 - 影响范围广
  6. Side Effects - 异步操作结果

优化关键点

  • 使用稳定的数据结构
  • 合理使用remember缓存
  • 状态局部化
  • 避免在Composable中创建对象
  • 提供稳定的key给列表项
相关推荐
Frank_HarmonyOS3 天前
Android APP 的压力测试与优化
android jetpack
QING6184 天前
Jetpack Compose 条件布局与 Layout 内在测量详解
android·kotlin·android jetpack
Lei活在当下4 天前
【现代 Android APP 架构】09. 聊一聊依赖注入在 Android 开发中的应用
java·架构·android jetpack
bqliang4 天前
Jetpack Navigation 3:领航未来
android·android studio·android jetpack
用户69371750013847 天前
🚀 Jetpack MVI 实战全解析:一次彻底搞懂 MVI 架构,让状态管理像点奶茶一样丝滑!
android·android jetpack
俩个逗号。。10 天前
ViewPager+Fragment 切换主题崩溃
android·android studio·android jetpack
alexhilton12 天前
Compose CameraX现已稳定:给Composer的端到端指南
android·kotlin·android jetpack
在狂风暴雨中奔跑14 天前
使用 Compose 权限请求模板高效搭建应用权限流程
android jetpack
H10019 天前
SharedFlow和StateFlow的方案选择-屏幕旋转设计
android jetpack
alexhilton19 天前
理解retain{}的内部机制:Jetpack Compose中基于作用域的状态保存
android·kotlin·android jetpack