为什么嵌套可组合项的所有父可组合项都会被重组?

Ken*_*iri 2 android android-jetpack-compose

我有一个简单的测试应用程序:

主要活动:

        ...
        setContent {
            TestTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    TestScreen()
                }
            }
        }
        ....
Run Code Online (Sandbox Code Playgroud)

测试屏幕:

@Composable
fun TestScreen(testViewModel: TestViewModel = viewModel()) {
    Log.i("Test", "A")
    Column {
        Log.i("Test", "B")
        Column {
            Log.i("Test", "C")
            Column {
                Log.i("Test", "D")
                OutlinedTextField(
                    value = testViewModel.textField,
                    onValueChange =  testViewModel::updateTextField
                )
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

测试视图模型:

class TestViewModel : ViewModel() {
    var textField by mutableStateOf("")
        private set

    fun updateTextField(v: String) {
        textField = v
    }
}
Run Code Online (Sandbox Code Playgroud)

对于 的每次键盘点击,我都会看到、、和OutlinedTextField的所有日志。我的期望是,在最初的组合之后我实际上不会看到任何日志,因为这是国家的唯一依赖。ABCDOutlinedTextFieldtextField

为什么每次击键时所有嵌套可组合项都会重新组合?

chu*_*ckj 6

Column是一个内联函数。Column非常简单,跳过它所需的代码比再次执行它的成本更高。

例如,在所提供的示例中没有 lambda 分配,尽管testViewModel所有 lambda 分配都被捕获。由于ColumnRow经常使用,因此这种节省对于整个应用程序来说非常重要。

一般来说,我建议您不要像这样优化重组,除非您手头有跟踪表明重组是导致卡顿的原因。通常,它是在惰性列表中创建新项目,避免细粒度跳过的开销可以减少卡顿。

如果您需要更多的跳过粒度,

  1. 将函数分解为更小的函数(不是inline)。
  2. 引入 aSkippable或类似的内容并用它包围需要跳过的块。
@Composable
fun Skippable(content: @Composable () -> Unit) = content()
Run Code Online (Sandbox Code Playgroud)

那么上面可以这样使用它,

@Composable
fun TestScreen(testViewModel: TestViewModel = viewModel()) {
    Log.i("Test", "A")
    Column {
        Skippable {
            Log.i("Test", "B")
            Column {
                Log.i("Test", "C")
                Column {
                    Log.i("Test", "D")
                    OutlinedTextField(
                        value = testViewModel.textField,
                        onValueChange =  testViewModel::updateTextField
                    )
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,因为您只是用来Column测试跳过,所以您可以替换ColumnSkippable

@Composable
fun TestScreen(testViewModel: TestViewModel = viewModel()) {
    Log.i("Test", "A")
    Skippable {
        Log.i("Test", "B")
        Skippable {
            Log.i("Test", "C")
            Skippable {
                Log.i("Test", "D")
                OutlinedTextField(
                    value = testViewModel.textField,
                    onValueChange =  testViewModel::updateTextField
                )
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)