Android Compose 使用 CompositionLocal 将数据的作用域限定在局部

文章目录

CompositionLocal 是 Jetpack Compose 中一个非常重要的机制,它允许你在组合树中隐式地向下传递数据,而不需要通过每个 Composable 函数的参数手动传递

为什么需要 CompositionLocal

在 Compose 中,通常通过参数将数据传递给子函数。但在复杂的应用中,某些数据(如主题颜色、字体样式、当前登录用户信息或配置信息)几乎每个 UI 组件都需要访问。

如果通过参数层层传递:

代码冗余:中间层的函数即使不需要这些数据,也必须定义参数并向下转发。

难以维护:一旦需要增加一个全局参数,可能需要修改数十个函数的签名。

CompositionLocal 解决了这个问题:你在高层提供一个值,底层的任何 Composable 都可以直接访问它。


核心概念与创建方式

创建 CompositionLocal 主要有两种方式:

  • A. compositionLocalOf

    特性:当提供的值发生改变时,Compose 只会让读取该值的 Composable 发生重组。

  • B. staticCompositionLocalOf

    特性:当值改变时,Compose 会重新读取并重组整个内容树,而不是只重组读取它的部分。

    优点:存储和读取的性能比 compositionLocalOf 略好(因为它不需要跟踪依赖关系)。

重要属性 current

kotlin 复制代码
class CompositionLocal {
	public inline val current: T
        @ReadOnlyComposable @Composable get() = currentComposer.consume(this)
}

示例

kotlin 复制代码
data class UserInfo(val name: String, val isAdmin: Boolean)

val LocalUserInfo = compositionLocalOf { UserInfo("default", false) }
// val LocalUserInfo = staticCompositionLocalOf { UserInfo("default", false) }
kotlin 复制代码
@Composable
fun CompLocalTest() {
		// state 记忆
    var currentUser by remember { mutableStateOf(UserInfo("Manager", true)) }

    // 通过 CompositionLocalProvider 向下传递,本质是定义了共享它的作用域
    CompositionLocalProvider(LocalUserInfo provides currentUser) {
        // 这里的子组件及其所有后代都可以访问到 currentUser
        Dashboard1()
        Dashboard2() // 内部使用 local.current
    }

    val user = LocalUserInfo.current
    Text(text = "hi, ${user.name}! Admin: ${user.isAdmin}",
        modifier = Modifier.padding(top = 40.dp))

    Button(onClick = {
        currentUser = UserInfo("xx", false)
    }, modifier = Modifier.padding(top = 100.dp)) {
        Text("change user:${currentUser}")
    }
}

@Composable
private fun Dashboard1() {
    SideEffect {
        Log.d("Dashboard1", "Dashboard1: 进入组合")
    }
}

@Composable
private fun Dashboard2() {
    SideEffect {
        Log.d("Dashboard2", "Dashboard2: 进入组合")
    }
    UserInfoLabel()
}

@Composable
private fun UserInfoLabel() {
    SideEffect {
        Log.d("UserInfoLabel", "UserInfoLabel: 进入组合")
    }
    // 获取当前作用域内的值
    val user = LocalUserInfo.current
    Text(text = "Welcome, ${user.name}! Admin: ${user.isAdmin}")
}
  • 通过 compositionLocalOfstaticCompositionLocalOf 创建的类型,它的直接或间接父类 是 ProvidableCompositionLocal,这个父类中提供了一个 中缀函数:
kotlin 复制代码
public infix fun provides(value: T): ProvidedValue<T> = defaultProvidedValue(value)

CompositionLocalProvider 函数接收一个 ProvidedValue<*> 类型的参数

kotlin 复制代码
public fun CompositionLocalProvider(value: ProvidedValue<*>, content: @Composable () -> Unit) {
    currentComposer.startProvider(value)
    content()
    currentComposer.endProvider()
}

这个参数类型就是上面的中缀函数的返回类型

  • SideEffect 每次进入组合阶段时都会调用。重组当然也会。
  • CompLocalTest() 功能:

currentUser 声明定义成 记忆状态,使用了 remember { mutableStateOf(...) }
CompositionLocalProvider 向内部 content 传递了 currentUser

界面中 第一个组件是 text = "Welcome ... 用户name是 "xx"

第二个 padding(top = 40.dp) 的Text显示的是 默认用户name是 "default"

第三个组件按钮 padding(top = 100.dp),显示的是 用户name是 "Manager"
Button 点击修改了 记忆状态对象 currentUser,引起了读取 currentUser 的可组合项发生重组。Button中的 Text("change user:${currentUser}") 、UserInfoLabel 中的 text = "Welcome ... 肯定会发生重组

若使用 ... = compositionLocalOf {},点击后只有这两处会发生重组。此时日志输出:

UserInfoLabel: 进入组合
若使用 ... = staticCompositionLocalOf {},点击后不光这两处会发生重组;CompositionLocalProvider的 content 内的 所有可组合项包括子项都会重组。此时日志输出:

Dashboard1: 进入组合

Dashboard2: 进入组合

UserInfoLabel: 进入组合


适用场景

compositionLocalOf:需要局部灵活变动、且变动频繁的数据。
staticCompositionLocalOf:需要局部灵活变动、但几乎一成不变的数据(如皮肤、样式)

相关推荐
逐光老顽童14 小时前
Java 与 Kotlin 混合开发避坑指南:30 个真实案例实录
android·kotlin
爱勇宝1 天前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
Yeyu1 天前
刷新一帧的艺术:invalidate / postInvalidate / postInvalidateOnAnimation全解析
android
潘潘潘1 天前
Android OTA 升级原理和流程介绍
android
plainGeekDev2 天前
null 判断 → Kotlin 可空类型
android·java·kotlin
plainGeekDev2 天前
getter/setter → Kotlin 属性
android·java·kotlin
YXL1111YXL2 天前
Handler 消息回收与协程异步执行的时序陷阱
android
恋猫de小郭2 天前
KMP / CMP 鸿蒙版本 Beta 发布,他有什么特别之处?
android·前端·flutter
三少爷的鞋2 天前
Android 协程并发控制:别动线程池,控制好并发语义就够了
android
weiggle2 天前
第七篇:状态提升与单向数据流——架构设计的核心
android