Composable Lambda为何未像非Composable Lambda一样被Memoize?
Compose编译器中Composable Lambda的Memoization疑问
我正在研究Compose编译器中Lambda的Memoization机制,聚焦于ComposerLambdaMemoization相关逻辑。根据我的理解:
- 对于非Composable Lambda,如果它仅捕获稳定值,编译器会自动用
remember调用包裹该Lambda,避免每次重组都创建新实例。 - 但这种自动记忆化的处理仅针对非Composable Lambda。
对于Composable Lambda,Compose编译器会创建一个合成类ComposableLambdaImpl,并通过remember保存这个实例。但每次重组时,Kotlin仍会分配新的Lambda实例,随后调用ComposableLambdaImpl.update方法将新实例传入。由于不同Lambda实例永远不会相等,ComposableLambdaImpl.update总会导致调用该Composable Lambda的重组作用域失效。
为验证这个假设,我编写了以下测试代码:
class ComposableLambdaActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val staticHeader = "Should composable lamdba be remembered?" val counter = remember { mutableIntStateOf(0) } val counterValue = counter.intValue LambdaMemoization( onClicked = { counter.intValue += 1 }, header = { Text(text = staticHeader) Log.e("ComposableLambdaTest", "Header recomposed!") } ) { Text(text = "Clicked $counterValue times!") } } } } @Composable fun LambdaMemoization( onClicked: () -> Unit, header: @Composable () -> Unit, body: @Composable () -> Unit ) { Log.e("ComposableLambdaTest", buildString { append("onClicked: ${onClicked}@${onClicked.hashCode()}\n") append("header: ${header}\n") append("body: ${body}\n") }) Column { header() Button(onClick = { onClicked() }) { body() } } }
多次点击按钮后,"Header recomposed!"日志被反复打印——尽管header Lambda仅捕获了一个静态字符串。我推测编译器生成的代码逻辑大致如下:
setContent { val staticHeader = "Should composable lamdba be remembered?" val counter = remember { mutableIntStateOf(0) } val counterValue = counter.intValue val nonComposableLambdaOnClick = remember(counter.intValue) {{ counter.intValue += 1 }} val composableLambdaHeader = { Text(text = staticHeader) Log.e("ComposableLambdaTest", "Header recomposed!") } val wrappedComposableLambdaHeader = remember { ComposableLambdaImpl(key, true, composableLambdaHeader) } wrappedComposableLambdaHeader.update(composableLambdaHeader) val composableLambdaBody = { Text(text = "Clicked $counterValue times!") } val wrappedComposableLambdaBody = remember { ComposableLambdaImpl(key, true, composableLambdaBody) } wrappedComposableLambdaBody.update(composableLambdaBody) LambdaMemoization( onClicked = nonComposableLambdaOnClick, header = wrappedComposableLambdaHeader, body = wrappedComposableLambdaBody ) }
可以看到composableLambdaHeader和composableLambdaBody并未被remember包裹,因此每次重组都会重新创建实例。我的疑问是:为什么Compose编译器不像处理非Composable Lambda那样,直接用remember包裹这些Composable Lambda?
内容的提问来源于stack exchange,提问作者darklord
相关产品推荐
相关产品推荐

