Jetpack Compose是个什么东西

522 阅读3分钟

Jetpack Compose 是用于构建原生 Android 界面的新工具包

您只需描述界面的外观,Compose 会负责完成其余工作

它与现有界面工具包兼容,因此您可以混合搭配使用经典视图和新视图,而且它从一开始就支持 Material 和动画。

Compose写法中,方法需要加上@Compose才行,所有可组合函数都必须带有此注释,此注释可告知 Compose 编译器:此函数旨在将数据转换为界面。

重组是指在输入更改时再次调用可组合函数的过程

通过跳过所有未更改参数的函数或 lambda,Compose 可以高效地重组。

组合函数中不应该去修改全局变量,如果其他组合函数依赖这个全局变量,那么就会有附带效应,组合函数的执行顺序就必须按到某个顺序来执行,这样不是Compose想要的。

如果您需要执行成本高昂的操作(例如从共享偏好设置读取数据),请在后台协程中执行,并将值结果作为参数传递给可组合函数。

当您在 Compose 中编程时,有许多事项需要注意:

  • 可组合函数可以按任何顺序执行。
  • 可组合函数可以并行执行。
  • 重组会跳过尽可能多的可组合函数和 lambda。
  • 重组是乐观的操作,可能会被取消。
  • 可组合函数可能会像动画的每一帧一样非常频繁地运行。

Compose 可以通过并行运行可组合函数来优化重组

应为可以在多线程中运行,所以函数写入局部变量,则这并非线程安全或正确的代码。因为在多个地方同时调用改组合函数的时候,界面可能会出现错乱:

@Composable
@Deprecated("Example with bug")
fun ListWithBug(myList: List<String>) {
    var items = 0

    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
                items++ // Avoid! Side-effect of the column recomposing.
            }
        }
        Text("Count: $items")
    }
}

这个代码中items多线程的时候可能出现两个2等情况,界面就会出现问题。

如果界面的某些部分无效,Compose 会尽力只重组需要更新的部分。这意味着,它可以跳过某些内容以重新运行单个按钮的可组合项,而不执行界面树中在其上面或下面的任何可组合项。

/**
 * Display a list of names the user can click with a header
 */
@Composable
fun NamePicker(
    header: String,
    names: List<String>,
    onNameClicked: (String) -> Unit
) {
    Column {
        // this will recompose when [header] changes, but not when [names] changes
        Text(header, style = MaterialTheme.typography.h5)
        Divider()

        // LazyColumn is the Compose version of a RecyclerView.
        // The lambda passed to items() is similar to a RecyclerView.ViewHolder.
        LazyColumn {
            items(names) { name ->
                // When an item's [name] updates, the adapter for that item
                // will recompose. This will not recompose when [header] changes
                NamePickerItem(name, onNameClicked)
            }
        }
    }
}

/**
 * Display a single name the user can click.
 */
@Composable
private fun NamePickerItem(name: String, onClicked: (String) -> Unit) {
    Text(name, Modifier.clickable(onClick = { onClicked(name) }))
}

当 header 发生更改时,Compose 可能会跳至 Column lambda,而不执行它的任何父项。此外,执行 Column 时,如果 names 未更改,Compose 可能会选择跳过 LazyColumnItems

只要 Compose 认为某个可组合项的参数可能已更改,就会开始重组。重组是乐观的操作,也就是说,Compose 预计会在参数再次更改之前完成重组。如果某个参数在重组完成之前发生更改,Compose 可能会取消重组,并使用新参数重新开始。

如果您的可组合函数需要数据,它应为相应的数据定义参数。然后,您可以将成本高昂的工作移至组成操作线程之外的其他线程,并使用 mutableStateOf 或 LiveData 将相应的数据传递给 Compose。