Android 中的可组合函数和普通函数有什么区别?

Hel*_*oCW 6 android android-jetpack-compose

我已经阅读了有关可组合函数的官方文档

但我无法理解可组合函数。
例如,rememberWatchState()Composable代码 A 中的一个函数,但它不像普通函数那样定义我的应用程序的 UI。

Android中的Composable函数和普通函数有什么区别?

代码A

@Composable
fun ScreenHome_Watch(
    watchState:WatchState =  rememberWatchState()
){
    val density= watchState.density
}


@Composable
fun rememberWatchState(): WatchState {
    val context: Context = LocalContext.current
    val temp = loadDBWarningValueWithPreference(context)

    val watchState = WatchState(temp.toFloat(),LocalDensity.current)

    return remember {
        watchState    
    }
}

class WatchState(
    private val alarmValue:Float,
    val density: Density
){
    ...
}


fun drawDial(
    drawScope: DrawScope,
    watchState:WatchState
) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

Thr*_*ian 11

@Composable注释就像一个范围,可以访问 Compose 函数(例如LaunchedEffectSideEffectremember或对象(例如currentComposer和 ),类似于挂起函数。

\n

以下部分引用自Leland Richardson 的Under the hood of Jetpack Compose \xe2\x80\x94 Part 2 of 2文章。

\n
// function declaration\nsuspend fun MyFun() { \xe2\x80\xa6 }\n \n// lambda declaration\nval myLambda = suspend { \xe2\x80\xa6 }\n\n// function type\nfun MyFun(myParam: suspend () -> Unit) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n

Kotlin\xe2\x80\x99s suspend 关键字作用于函数类型:您可以有一个\xe2\x80\x99s 挂起的函数声明、lambda 或类型。Compose 以同样的方式工作:它可以改变函数类型。

\n
// function declaration\n@Composable fun MyFun() { \xe2\x80\xa6 }\n \n// lambda declaration\nval myLambda = @Composable { \xe2\x80\xa6 }\n \n// function type\nfun MyFun(myParam: @Composable () -> Unit) { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n

这里重要的一点是,当您使用@Composable注释函数类型时,您\xe2\x80\x99正在更改其类型:没有注释的相同函数类型与注释类型不兼容。此外,挂起函数需要调用上下文,这意味着您只能在另一个挂起函数内部调用挂起函数。

\n
fun Example(a: () -> Unit, b: suspend () -> Unit) {\n   a() // allowed\n   b() // NOT allowed\n}\n \nsuspend \nfun Example(a: () -> Unit, b: suspend () -> Unit) {\n   a() // allowed\n   b() // allowed\n}\n
Run Code Online (Sandbox Code Playgroud)\n

可组合的工作方式相同。这是因为有\xe2\x80\x99s 一个调用上下文对象,我们需要通过所有调用来线程化。

\n
fun Example(a: () -> Unit, b: @Composable () -> Unit) {\n   a() // allowed\n   b() // NOT allowed\n}\n \n@Composable \nfun Example(a: () -> Unit, b: @Composable () -> Unit) {\n   a() // allowed\n   b() // allowed\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我们\xe2\x80\x99传递的调用上下文是什么?为什么我们需要这样做?

\n

我们称这个对象为Composer. Composer 的实现包含与 Gap Buffer 密切相关的数据结构。这种数据结构通常用于文本编辑器。

\n

让\xe2\x80\x99s 看一下计数器的示例。

\n
@Composable\nfun Counter() {\n var count by remember { mutableStateOf(0) }\n Button(\n   text="Count: $count",\n   onPress={ count += 1 }\n )\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是我们要编写的代码,但让\xe2\x80\x99s 看看编译器会做什么。

\n

当编译器看到 Composable 注释时,它会插入附加参数并调用函数体。

\n

首先,编译器添加对composer.start方法的调用,并向其传递编译时生成的关键整数。

\n
fun Counter($composer: Composer) {\n $composer.start(123)\n var count by remember { mutableStateOf(0) }\n Button(\n   text="Count: $count",\n   onPress={ count += 1 }\n )\n $composer.end()\n}\n
Run Code Online (Sandbox Code Playgroud)\n

例如,LazyRow 或 LazyColumns 的 LazyListScope 接收器内容未使用 @Composable 注释,这不允许您调用其他可组合项

\n
@Composable\nfun LazyColumn(\n    // Rest of params\n     // Non Composable scope\n    content: LazyListScope.() -> Unit\n) {\n  // Implementation details\n}\n\nLazyColumn(){ // LazyListScope\n   // You can\'t add Composable here because this scope is LazyListScope\n   // which is not annotated with @Composable, and returns compiler error\n   // Row(){}\n   \n   items(100){\n       Row() {\n           \n       }\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您不能从中调用 Composable 函数,LazyListScope但可以在内部调用,items因为它的内容是 @Composable 函数。

\n
fun item(\n    key: Any? = null,\n    contentType: Any? = null,\n    content: @Composable LazyItemScope.() -> Unit\n) {\n    error("The method is not implemented")\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您需要一个带有 @Composable 注释的作用域来调用其他 @Composable 函数。

\n

可组合函数并不强制必须是 UI 函数。某些函数(例如SideEffect或 )LaunchedEffect用于在您的 Composable 函数成功进入组合时运行。

\n
@Composable\n@NonRestartableComposable\n@OptIn(InternalComposeApi::class)\nfun SideEffect(\n    effect: () -> Unit\n) {\n    currentComposer.recordSideEffect(effect)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

或者不仅检查它何时进入合成,还检查它何时退出DisposableEffect

\n
@Composable\nprivate fun NonUIComposableSample() {\n\n    val context = LocalContext.current\n\n    var counter by remember { mutableStateOf(0) }\n    var color by remember { mutableStateOf(Color.Red) }\n\n    if (counter in 3..5) {\n        DisposableEffect(Unit) {\n\n            Toast.makeText(context, "Entering Composition counter: $counter", Toast.LENGTH_SHORT).show()\n            color = Color.Yellow\n            onDispose {\n                color = Color.Green\n                Toast.makeText(context, "Exiting Composition counter: $counter", Toast.LENGTH_SHORT).show()\n            }\n        }\n    }\n\n    Button(onClick = { counter++ }) {\n        Text("Counter: $counter", color = color)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在此示例中,具有 DisposableEffect 的块在计数器为 3 时进入合成,并在不再满足条件时退出。

\n

另外,productState是另一个 @Composable 非 ui 函数,它启动一个作用范围为 Composition 的协程,可以将值推送到返回的 State 中。使用它将非 Compose 状态转换为 Compose 状态,例如将 Flow、LiveData 或 RxJava 等外部订阅驱动状态引入 Composition。

\n

Compose 是一种数据结构,保存了组合中的所有对象,整个树按执行顺序排列,实际上是对树的深度优先遍历。

\n