Jetpack Compose -> 重组的性能风险和优化

本文讨论了JetpackCompose中的重组性能问题,特别是mutableStateOf引发的大量ReCompose可能导致资源浪费。文章介绍了如何通过避免无谓的函数执行和使用@Stable注解来优化性能,并强调了可靠类和不可靠类在ReCompose中的行为差异。

前言


上一章我们讲解了 Jetpack Compose -> mutableStateOf 状态机制的背后秘密 本章我们讲解下重组的性能风险以及怎么优化;

重组的性能风险


前面我们一直在讲重组(ReCompose) 的过程,在使用 mutableStateOf() 以及对于 List 和 Map 在使用 mutatbleStateListOf()、mutableStateMapOf() 也能监听到内部状态变化,如果对于 List 和 Map 使用的是 mutableStateOf() 只能触发这个对象变化的监听;

重组其实分为:触发重组和重组,这是两个过程,触发重组是某个变量发生改变之后,Compose 去把已经组合好的那些部分重新的 Compose 一次,这个所谓的组合好的部分就是之前说的组合过程的结果;也就是那个稍后被拿去组合、测量、绘制的结果,它在相关的变量改变之后,是需要重新组合过程,重新生成结果的;

触发重组,就是 ReCompose Scope 重组范围;重组是在下一帧的时候去调用这些失效了的 compose 代码,来重新生成组合的过程;

因为 Column 函数是一个内联函数,所以 Column 函数在编译后会被抹除掉,也就是说,如果我们在使用下面的逻辑的时候

private var number by mutableStateOf(1)

@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Android_VMTheme {
            Surface {
                println("Recompose 测试范围1")
                Column {
                    println("Recompose 测试范围2")
                    Text(text = "当前数值是: $number", modifier = Modifier.clickable {
                        number ++
                    })
                }
            }
        }
    }
}

Column 会被抹除掉,而是直接将 Text 放到那里,也就是说如果发生了 ReCompose 的时候,是会把 Column 前后范围内的都会触发 Recompose;

也就是说当我们点击 Text 触发 number++ 的时候,会再一次打印 “Recompose 测试范围1” 和 “Recompose 测试范围2”;

这其实就是重组的性能风险;一个小的改动,触发了大面积的 ReCompose,这就造成了计算资源浪费;

我们来继续验证下这个结论,假设我们有下面这样的一段代码

private var number by mutableStateOf(1)

@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Android_VMTheme {
            Surface {
                println("Recompose 测试范围1")
                Column {
                    testPerformance()
                    println("Recompose 测试范围2")
                    Text(text = "当前数值是: $number", modifier = Modifier.clickable {
                        
jetpack compose 性能优化下面代码 //直播功能控制区 @Composable private fun LiveFunctionalControlView( modifier: Modifier = Modifier, state: DeviceLiveViewModel.State, callback: (DeviceLiveViewModel.Intent) -> Unit ) { //1.分离按钮创建逻辑 //分辨率 val resolutionButton = remember(state.resolution) { ImageTextButton( label = R.string.清晰度, icon = if (state.resolution == LiveMediaSource.Resolution.FHD) R.drawable.fhd else R.drawable.hd, state = if (state.resolution == LiveMediaSource.Resolution.FHD) RTImageTextButtonState.ACTIVATED else RTImageTextButtonState.ENABLED ) { callback(DeviceLiveViewModel.Intent.SwitchResolution) } } //horizontalFlipButton:水平镜像 verticalFlipButton:垂直翻转 val (horizontalFlipButton, verticalFlipButton) = remember(state.currentFlip) { val hFlipButton = ImageTextButton( label = R.string.水平翻转, icon = R.drawable.flip_left_right_on, state = if (state.currentFlip == MediaRender.FlipMode.MIRROR || state.currentFlip == MediaRender.FlipMode.FLIP_MIRROR) RTImageTextButtonState.ACTIVATED else RTImageTextButtonState.ENABLED, ) { val value = when (state.currentFlip) { MediaRender.FlipMode.NORMAL -> MediaRender.FlipMode.MIRROR MediaRender.FlipMode.MIRROR -> MediaRender.FlipMode.NORMAL MediaRender.FlipMode.FLIP -> MediaRender.FlipMode.FLIP_MIRROR MediaRender.FlipMode.FLIP_MIRROR -> MediaRender.FlipMode.FLIP } callback(DeviceLiveViewModel.Intent.UpdateFlip(value)) } val vFlipButton = ImageTextButton( label = R.string.垂直翻转, icon = R.drawable.flip_up_down_on, state = if (state.currentFlip == MediaRender.FlipMode.FLIP || state.currentFlip == MediaRender.FlipMode.FLIP_MIRROR) RTImageTextButtonState.ACTIVATED else RTImageTextButtonState.ENABLED ) { val value = when (state.currentFlip) { MediaRender.FlipMode.NORMAL -> MediaRender.FlipMode.FLIP MediaRender.FlipMode.MIRROR -> MediaRender.FlipMode.FLIP_MIRROR MediaRender.FlipMode.FLIP -> MediaRender.FlipMode.NORMAL MediaRender.FlipMode.FLIP_MIRROR -> MediaRender.FlipMode.MIRROR } callback(DeviceLiveViewModel.Intent.UpdateFlip(value)) } hFlipButton to vFlipButton } //日夜模式 val nightVisionButton = remember(state.nightVisionMode) { ImageTextButton( label = R.string.日夜模式, icon = if (state.nightVisionMode == NightVisionRequest.Mode.OPEN) R.drawable.btn_ircut_on else R.drawable.btn_ircut_off, state = if (state.nightVisionMode == NightVisionRequest.Mode.OPEN) RTImageTextButtonState.ACTIVATED else RTImageTextButtonState.ENABLED ) { val value = when (state.nightVisionMode) { NightVisionRequest.Mode.AUTO -> NightVisionRequest.Mode.OPEN NightVisionRequest.Mode.OPEN -> NightVisionRequest.Mode.AUTO NightVisionRequest.Mode.CLOSE -> NightVisionRequest.Mode.OPEN } callback(DeviceLiveViewModel.Intent.NightVision(value)) } } //led指示灯 val ledButton = remember(state.led) { ImageTextButton( label = R.string.指示灯, icon = if (state.led == LedRequest.LedMode.OPEN) R.drawable.btn_led_on else R.drawable.btn_led_off, state = if (state.led == LedRequest.LedMode.OPEN) RTImageTextButtonState.ACTIVATED else RTImageTextButtonState.ENABLED ) { callback(DeviceLiveViewModel.Intent.Led) } } //wled白光灯 val wledButton = remember(state.wled) { ImageTextButton( label = R.string.白光灯, icon = if (state.wled == WledRequest.WledMode.AUTO) R.drawable.btn_wled_on else R.drawable.btn_wled_off, state = if (state.wled == WledRequest.WledMode.AUTO) RTImageTextButtonState.ACTIVATED else RTImageTextButtonState.ENABLED ) { val value = when (state.wled) { WledRequest.WledMode.AUTO -> WledRequest.WledMode.OPEN WledRequest.WledMode.OPEN -> WledRequest.WledMode.AUTO WledRequest.WledMode.CLOSE -> WledRequest.WledMode.OPEN } callback(DeviceLiveViewModel.Intent.Wled(value)) } } // 2. 组合缓存 val imageTextButtons = remember( state.capability, resolutionButton, horizontalFlipButton, verticalFlipButton, nightVisionButton, ledButton, wledButton ) { val buttons = mutableListOf<ImageTextButton>() buttons.add(resolutionButton) buttons.add(horizontalFlipButton) buttons.add(verticalFlipButton) if (state.capability.controlircut != 0) buttons.add(nightVisionButton) if (state.capability.controlled != 0) buttons.add(ledButton) if (state.capability.controlwled != 0) buttons.add(wledButton) buttons } LazyVerticalGrid(modifier = modifier, columns = GridCells.Fixed(4)) { imageTextButtons.forEach { item -> item { Box { RTImageTextButton( modifier = Modifier .padding(8.dp) .align(Alignment.Center), label = stringResource(item.label), iconPainter = painterResource(item.icon), state = item.state ) { item.onClick() } } } } } }
最新发布
11-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值