如何调试JetPack通过记录组成重新组件?
#初学者 #kotlin #android #jetpackcompose

调试JetPack组成重新组件的最佳方法是什么?调试器,标准记录(即log.d)中的断点,或者我们需要自定义记录?

JetPack组成中的重新分配是一个复杂的主题。这很复杂,因为有时您不知道为什么要重新组建某个功能,这不是您根据知识所期望的。因此,您需要调试。

调试器中的断点

首先在调试器中使用断点进行调试重新组件。但是,这种方法有一些局限性。

  • 它不会告诉您重新组件范围信息(即$currentRecomposeScope-参见下文)

  • 它不会跟踪已经发生了多少个重新组件

标准记录

因此,要通过记录进行调试,您可以使用Log.d。看起来像这个

Log.d("DebugRecomposition", "RecompositionExample() function scope")

但它错过了一个重要的信息,该信息并未告诉当前的重新组件范围信息。此信息很重要,因为不同的合并功能仍然可以具有相同的重新组件范围 - 请参见下面的说明。

要打印此重建范围信息,您可以使用$currentRecomposeScope。现在,伐木看起来像这样

Log.d("DebugRecomposition", "RecompositionExample() function scope $currentRecomposeScope")

日志输出看起来像这样:

D/DebugRecomposition: RecompositionExample() function androidx.compose.runtime.RecomposeScopeImpl@894fab8

RecomposeScopeImpl@894fab8是此重新组合范围的唯一ID。如果另一个可复合函数具有相同的唯一ID,则意味着它也属于同一重建范围。

好吧,仍然有一条缺失的信息 - 重新组件计数。从技术上讲,您仍然可以手动计算日志语句,但这非常麻烦且容易出错。因此,您需要自定义记录。

自定义记录

我从这个非常好的帖子中窃取了关于重新编写的here的自定义记录代码,我对此进行了一些修改,因为我认为有些东西是不必要的。

这是修改版本:

class RecompositionCounter(var value: Int)

@Composable
inline fun LogCompositions(tag: String, msg: String) {
    if (BuildConfig.DEBUG) {
        val recompositionCounter = remember { RecompositionCounter(0) }

        Log.d(tag, "$msg ${recompositionCounter.value} $currentRecomposeScope")
        recompositionCounter.value++
    }
}
  • 我更名为Ref class RecompositionCounter to更好地反映这是重新组件计数

  • 我删除了SideEffect {}并在登录后移动计数器增量。我认为我们不需要koude6 here。

  • 我添加了$currentRecomposeScope作为我认为重要的其他信息。

inline是为了确保调用此组合函数的父母具有相同的合并函数范围。换句话说,当父母重新组建父时,此LogCompositions()功能肯定会被调用。

例子

让我们看下面的一个简单示例。

@Composable
fun RecompositionExample() {
    var count by remember { mutableStateOf(0) }

    LogCompositions("DebugRecomposition", "RecompositionExample() function scope")

    Column {

        LogCompositions("DebugRecomposition", "Column() content scope")

        MyButton(onClick = { count++ }, text = count.toString())

    }
}

@Composable
fun MyButton(
    onClick: () -> Unit,
    text: String) {

    LogCompositions("DebugRecomposition", "MyButton() function")

    Button(onClick = onClick) {

        LogCompositions("DebugRecomposition", "Button() content")

        Text(
            text = text,
        )
    }
}

MyButtonButton()的包装器,其中Text()在其内容lambda中。

请注意功能范围内容范围

  • 功能范围是函数内部的范围。

    内容范围是尾声范围,该功能的最后一个lambda参数。

日志输出在启动过程中看起来像这样:

D/DebugRecomposition: RecompositionExample() function scope 0 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: Column() content scope 0 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: MyButton() function 0 androidx.compose.runtime.RecomposeScopeImpl@399bf6

D/DebugRecomposition: Button() content 0 androidx.compose.runtime.RecomposeScopeImpl@dc1e8e2
  • 您注意到RecompositionExample()Column()具有相同的重建范围。这是因为常见布局,例如Column()Row()Box()都是“内联”综合函数。因此,他们具有与呼叫者相同的范围。

如果单击按钮,日志输出如下:

D/DebugRecomposition: RecompositionExample() function scope 1 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: Column() content scope 1 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: MyButton() function 1 androidx.compose.runtime.RecomposeScopeImpl@399bf6

D/DebugRecomposition: Button() content 1 androidx.compose.runtime.RecomposeScopeImpl@dc1e8e2
  • 单击按钮时,count状态被突变。因此,所有重新组合读取状态的范围都将被重新组成。

  • column()范围中,它读取text = count.toString()count状态。因此,column()被重新组成。因为column()RecompositionExample()具有相同的重建范围,所以RecompositionExample()也已重新组件。

  • MyButton()由于更改了输入参数text而被重新组建。读取text的范围将被重新组成。因此,Button()Text()也被重新组件。 Text()没有日志记录,因此它不会显示在日志中。

结论

如前所述,重新组合是一个复杂的话题。本文不关注为什么以及如何进行重新分配。它在上面的示例中涵盖了一点,但这只是一个相当基本的演示。

本文展示了如何使用自定义记录LogCompositions()来弄清楚重新组件的行为。我认为,重新组成是掌握JetPack组成的最重要的概念,了解其工作原理是至关重要的。

源代码

github存储库:Demo_UnderstandComposeConcept


最初出版于https://vtsen.hashnode.dev