1 Compose 基础概念

506 阅读4分钟

Compose 基础概念

Compose 是 Android 声明式的状态驱动 UI 工具包, 通过定义一组可组合函数(composable functions)来创建界面,这些函数接收状态(参数)并在函数内部描述UI。

可组合函数(Composable functions)

  1. 使用 @Composable 注解修饰
  2. 没有返回值,因为在其内部描述UI ,而不是生成 UI 控件
  3. Composable 函数应该是纯函数(幂等,无副作用)
  4. 使用 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")
}

image.png 重组是乐观操作,当重组没有结束,使其重组的状态又发生了变化,取消没结束的重组使用最新状态再次重组。

Composeable 的生命周期

Compose 通过初始组合生成 Composeable 树, 在状态发生变化时重组再次执行 Composeable 函数,重组是改变组合的唯一方法。

在 Composeable 的生命周期中会进行 1 次初始组合和 0 到 n 次重组

384686FA-BDC3-4882-9C2E-C70DAEE8A804.png

在 Composition 中每次调用 Composeable 函数都会生成一个 Composeable 实例,每一个 Composeable 实例都有自己的生命周期。

调用点(Call Site) : Composeable 函数在代码中调用的位置。影响其在 Composition 中的位置,因此会影响 UI 树。

Compose 编译器会把每个  Composeable 实例标记成不同的调用点。重组时通过调用点识别哪些 Composeable 函数已经被调用,哪些没有被调用。已经调用过又没有状态变化的 Composeable 函数不会重组。

Compose 的阶段

Compose 渲染每一帧分为三个阶段

  1. 组合(Composition): 生成 UI 树
  2. 布局(Layout): 测量树中每个元素的大小,确定每个元素的位置
  3. 绘制(Drawing) : 将树中每个元素绘制到画布中

image.png

总结:

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 
}

image.png

每个阶段都会进行状态读取

0A2D5D0E-E4F6-4408-99BE-ABD268553C21.png

尽量延迟状态的读取使得 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,拿不准的地方中文版 + 英文版混着看。