Compose 基础概念
Compose 是 Android 声明式的状态驱动 UI 工具包, 通过定义一组可组合函数(composable functions)来创建界面,这些函数接收状态(参数)并在函数内部描述UI。
可组合函数(Composable functions)
- 使用 @Composable 注解修饰
- 没有返回值,因为在其内部描述UI ,而不是生成 UI 控件
- Composable 函数应该是纯函数(幂等,无副作用)
- 使用 kotlin 作为底层语言,可以使用所有的 kotlin 语法,享受 kotlin 语法灵活强大的同时也可能会让函数更复杂。
组合(Composition)
组合(Composition): Compose 执行 Composable 函数,生成一个 Composable 树( UI 树),根据这个树形结构来描述 UI 界面。
状态(State)
状态(State)是指 UI 界面显示中可变的数据。
Compose 更新 UI 的唯一方式就是使用新参数重新执行 Composable 函数。这些可变的参数就是 Compose 中的状态。
Compose 中状态为 State 类型 ,value 属性代表状态的具体值,改变 value 的值就是改变状态。
- State 类型,value 值不可变一般作为方法参数类型声明。
- MutableState 类型继承 State,value 值可变一般作为实参声明。
@Composable
fun TestFunWithState(b: State<Int>) {}
@Composable
fun TestContainer() {
val b = remember { mutableStateOf(0) } //创建MutableState<Int> b
TestFunWithState(b = b)
Button(onClick = { b.value +=1 }) { Text("b++") } //更改 b 的 value 触发重组
}
支持 LiveData、Flow 、RxJava2 转换成 State<T>
状态提升指
Composable 函数将状态声明成参数,函数体内是完成状态不同值的具体实现,将状态的控制交给调用者。
状态读取
Compose 会自动跟踪读取 State.value 的操作。在 State.value 发生变时重新读取 State.value 的值,通过跟踪实现了 Compose 中的状态观察。
初始组合、 重组
- 初始组合(Initial composition): 第一次 Composition,生成 Composable 树。
- 重组(Recomposition): 当状态发生变化时,Compose 会使用新状态再次调用 Composable 函数,使其可以根据新的状态重新描述UI。
@Composable
fun TestContainer() {
Log.e(TAG, "Container Composing Start")
var b by remember { mutableStateOf(0) }
Column {
TestFunWithoutState()
TestFunWithState(b = b)
Button(onClick = {
Log.e(TAG, "============================")
b++
}) {
Text(text = "b++")
}
}
Log.e(TAG, "Container Composing End")
}
@Composable
fun TestFunWithState(b: Int) {
Log.e(TAG, " TestFunWithState Composing Start")
Text(text = "1 + $b = ${1 + b}")
Log.e(TAG, " TestFunWithState Composing End")
}
@Composable
fun TestFunWithoutState(){
Log.e(TAG, " TestFunWithoutState Composing Start")
Text(text = "TestFunWithoutState")
Log.e(TAG, " TestFunWithoutState Composing End")
}
重组是乐观操作,当重组没有结束,使其重组的状态又发生了变化,取消没结束的重组使用最新状态再次重组。
Composeable 的生命周期
Compose 通过初始组合生成 Composeable 树, 在状态发生变化时重组再次执行 Composeable 函数,重组是改变组合的唯一方法。
在 Composeable 的生命周期中会进行 1 次初始组合和 0 到 n 次重组
在 Composition 中每次调用 Composeable 函数都会生成一个 Composeable 实例,每一个 Composeable 实例都有自己的生命周期。
调用点(Call Site) : Composeable 函数在代码中调用的位置。影响其在 Composition 中的位置,因此会影响 UI 树。
Compose 编译器会把每个 Composeable 实例标记成不同的调用点。重组时通过调用点识别哪些 Composeable 函数已经被调用,哪些没有被调用。已经调用过又没有状态变化的 Composeable 函数不会重组。
Compose 的阶段
Compose 渲染每一帧分为三个阶段
- 组合(Composition): 生成 UI 树
- 布局(Layout): 测量树中每个元素的大小,确定每个元素的位置
- 绘制(Drawing) : 将树中每个元素绘制到画布中
总结:
Composable 函数应该是纯函数
使得 无序/并行 执行 Composable 函数并不会对最终结果产生应该,也是重组可以跳过无状态或使用的状态没有发生变化的Composable 函数和 lambdas 的根本。
Composable 函数可能会被调用的很频繁
避免在 Composable 函数中进行计算等耗时操作
跟踪状态读取的位置
状态声明在哪 Compose 不关心, 状态读取的位置会影响重组时执行 Composable 函数的范围
@Composable
fun TestFunWithState(b: Int) {}
@Composable
fun TestContainer() {
//下面 自动调用 value get set 都是需要导包的
var b by remember { mutableStateOf(0) } // by 调用 value get 为 b 赋值
TestFunWithState(b = b)
Button(onClick = { b++ }) { Text("b++") } //更改 b 的会调用到 value 的 set
}
每个阶段都会进行状态读取
尽量延迟状态的读取使得 Compose 可以略过更多的阶段。
例如根据不同的状态显示不同的背景, Composition 和 Layout 阶段都没有变化,将状态读取放到 Drawing 阶段这样回略过前面两个阶段
更多优化 developer.android.com/jetpack/com…
参考:
developer.android.com/jetpack/com…
developer.android.com/jetpack/com…
developer.android.com/jetpack/com…
developer.android.com/jetpack/com…
developer.android.com/jetpack/com…
建议看英文的有些术语翻译过来可能不认识了 0.0,拿不准的地方中文版 + 英文版混着看。