Jetpack Compose性能调优基础实践

背景

在本篇文章中,我们将探讨在Jetpack Compose中提升应用性能的基础实践,重点关注那些应当采取与避免的操作。借助具体的代码示例,我们将揭示如何通过代码中的微小调整,实现应用程序效率和性能的显著增强。

Stable&Immutable

首先是使用Stable和Immutable注解来避免非要的重组。

在Jetpack Compose中,如果一个类中包含了可变变量(var)或可变集合(例如ArrayList),编译器会默认视该类为不稳定的。这就导致Compose可能会频繁地对这些类进行重新组合,以防它们的状态可能已经改变。然而,通过使用这两个特定的注解,我们可以防止这种不必要的重新组合。

Stable

@Stable注解是一个承诺,向Compose编译器表明这个对象可能会发生变化。但每当对象发生变化时,我们会保证Compose运行时都会得到通知。因此,Compose运行时可以将其视为稳定的类,从而避免非必要的重组。让我们来看一个例子:

@Stable
class ProductListState(
  val products: List<Product>,
  val isLoading: Boolean
)

//usage
@Composable
fun ShowProducts(
    modifier: Modifier = Modifier,
    productListState: ProductListState
) {
    LazyColumn(modifier = modifier) {
        items(productListState.products) {
            Text(text = it.name)
        }
    }
}

Immutable

@Immutable用于标记完全不可变的类。一旦创建了此类的一个对象,其中的任何内容都不能在这个对象中改变,从而减少了不必要的重新组合。

注意:如果您向列表中添加或删除一个项目,由于您使用了“@Immutable”,组合件将永远不会被重新创建。

@Immutable
class ProductListState(
    val products: List<Product>,
)

@Composable
fun ShowProducts(
    modifier: Modifier = Modifier,
    productListState: ProductListState
) {
    LazyColumn(modifier = modifier) {
        items(productListState.products) {
            Text(text = it.name)
        }
    }
}

在上面这个例子中,我们使用Immutable来标记ProductListState类,这样即使它包含了List类型的参数,它也不会被编译器视为稳定类,从而在重组中跳过。

Key关键字

第二个就是需要为每个列表项提供Key作为唯一指引。这样在数据发生改动时,Compose可以自动保留未改变的列表项,减少更新范围。

我们先看一个没有使用Key的例子:

@Composable
fun NotesList(notes: List<Note>) {
    LazyColumn {
        items(
            items = notes
        ) { note ->
            NoteRow(note)
        }
    }
}

上面这个示例在正常情况下并没有什么问题,列表可以正常展示。但是如果我们对数据列表做出一些改动,将最后一个item移至第一个。在这种情况下,其实数据列表的内容并没有改变,只是顺序有一个调整。但是对于Compose编译器来说,它并没有办法识别到这个情况。它只能感知到每个列表项的数据都发生了改变,所以它会触发全量重组。这就会导致性能问题,因为实际上只有一个列表项发生了改变。

正确的做法应该是为每个列表项指定一个Key作为唯一标识,这样Compose编译器就能区分是数据改变还是仅仅是数据位置发生了改动:

@Composable
fun NotesList(notes: List<Note>) {
    LazyColumn {
        items(
            items = notes,
            key = { note ->
                // 需要提供一个唯一标识
                note.id
            }
        ) { note ->
            NoteRow(note)
        }
    }
}

副作用

第三点就是避免非必要的副作用。有时我们需要在组合式内部操作状态,但是当组合式修改状态时,就会发生不必要的副作用,这可能导致不可预测的行为和意外的重新组合。让我们来看一个例子:

@Composable
fun ExempleSideEffect() {
   val state = remember { mutableStateOf(0) }
   // Side effect: manage state inside composable
    Button(onClick = { state.value++ }) {
        Text("Click here")
    }
     //another Side effect
     LaunchedEffect(key1 = state.value) {
         Log.d("Exemple", "State changes to ${state.value}")
     }
}

上面是一个错误示例,我们在Composable中使用LaunchedEffect启动了一个副作用,它以state.value作为Key。然而,我们又添加了一个button,它在被点击时会改变state.value。这样就会导致每次按钮点击时都会导致整个Composable重组,影响了性能。

正确的做法应该如下:

@Composable
fun ExempleCorrectSideEffect(onClickIncrement: () -> Unit, count: Int) {
    // callback to handle the click button
    Button(onClick = onClickIncrement) {
        Text("Click here")
    }
    // shows the counter value 
    Text("Count: $count")
}

可以看到,我们将状态提升到了上层,使该Composable成为了无状态方法。并且,我们去除了多余的副作用。这样,我们就避免了全量重组的问题。

图片加载优化

最后一个就是图片加载的优化。我们可以通过以下几点来提升图片的加载性能及用户体验:

  • 只有在需要时才加载图片,减少内存使用并提高应用程序的加载时间。

  • 通过避免加载不在屏幕上的图片来节省资源。

  • 与第三方的库(如Coil和Glide)集成,实现高效的图片管理。

让我们来看一个例子:

@Composable
fun LoadImageWithouLazyLoading(url: String) {
    val bitmap = loadImageBitmap(url) // 耗时函数
    Image(
        bitmap = bitmap,
        contentDescription = null,
        modifier = Modifier.fillMaxWidth()
    )
}

可以看到,上面的例子中使用了一个耗时函数来加载图片。这样的写法会导致每次重组时都调用耗时函数,获取到图片内容后再展示,这就会大大影响性能和用户体验。正确的做法应该如下:

@Composable
fun LazyLoadingWithCoil(url: String) {
    Image(
        painter = rememberImagePainter(
            data = url,
            builder = {
                crossfade(true)
            }
        ),
        contentDescription = null,
        modifier = Modifier.fillMaxWidth()
    )
}

这里,我们使用了Coil库提供的rememberImagePainter来加载图片。这个方法会缓存图片的加载结果,这样在后续的重组中就可以直接复用,无需重新加载,大大提高了性能。

总结

在这篇文章中,我们探讨了几种优化Jetpack Compose性能的关键策略。通过在开发过程中采纳良好的实践,你可以显著提高Android应用程序的效率和响应性。希望它们对您有所帮助,谢谢!

  • 34
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值