You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose的LazyColumn中异步加载通知大图标Drawable?

解决Jetpack Compose LazyColumn加载通知大图标卡顿问题

你的问题核心是主线程执行Drawable加载与Bitmap转换的耗时操作,加上LazyColumn重组时重复触发加载,导致列表滑动卡顿。以下是两种可行的异步加载实现方案:

原生协程异步实现

通过Compose的协程API将回调式加载转为挂起函数,把耗时操作放到后台线程执行,同时避免重组重复触发加载:

@Composable
fun ListElement(notify: Notification) {
    val context = LocalContext.current
    val largeIcon = notify.getLargeIcon()
    // 以大图标为key,确保仅在图标变化时重新加载
    val iconBitmapState = remember(largeIcon) { mutableStateOf<ImageBitmap?>(null) }

    Row(
        modifier = Modifier
            .height(80.dp)
            .fillMaxWidth()
            .padding(start = 10.dp, top = 2.dp, bottom = 0.dp, end = 10.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Column {
            Text(
                text = notify.extras.getString(Notification.EXTRA_TITLE, ""),
                style = MaterialTheme.typography.titleMedium
            )
            Text(text = notify.extras.getString(Notification.EXTRA_TEXT, ""))
        }

        largeIcon?.let { icon ->
            // 仅在图标变化时启动协程加载
            LaunchedEffect(icon) {
                withContext(Dispatchers.IO) {
                    // 将回调转为挂起函数,避免回调嵌套
                    val drawable = suspendCancellableCoroutine<Drawable> { cont ->
                        icon.loadDrawableAsync(
                            context = context,
                            listener = object : Icon.OnDrawableLoadedListener {
                                override fun onDrawableLoaded(drawable: Drawable) {
                                    cont.resume(drawable)
                                }
                            },
                            handler = Handler(Looper.getMainLooper())
                        )
                    }
                    // 后台线程完成Bitmap转换,避免阻塞主线程
                    val bitmap = drawable.toBitmap()
                    // 切回主线程更新UI状态
                    withContext(Dispatchers.Main) {
                        iconBitmapState.value = bitmap.asImageBitmap()
                    }
                }
            }

            // 根据状态显示图标或占位符
            iconBitmapState.value?.let { bitmap ->
                Spacer(modifier = Modifier.height(10.dp))
                Image(
                    bitmap = bitmap,
                    contentDescription = "通知图标",
                    modifier = Modifier.fillMaxHeight()
                )
            } ?: Image(
                painter = painterResource(id = R.drawable.default_notify_icon),
                contentDescription = "加载中图标",
                modifier = Modifier.fillMaxHeight()
            )
        }
    }
}

关键优化点

  • LaunchedEffect(largeIcon):仅在图标实例变化时执行加载逻辑,避免Compose重组重复触发
  • suspendCancellableCoroutine:将系统提供的回调式API转为协程挂起函数,适配Compose的生命周期
  • Dispatchers.IO:把Drawable转Bitmap的耗时操作放到后台线程,不阻塞主线程
  • 添加占位符:避免列表项因加载延迟出现空白,提升滑动体验

第三方库简化实现(推荐)

使用Coil(专为Compose优化的图片加载库),它会自动处理异步加载、缓存、线程调度和列表复用,代码更简洁:

@Composable
fun ListElement(notify: Notification) {
    val largeIcon = notify.getLargeIcon()

    Row(
        modifier = Modifier
            .height(80.dp)
            .fillMaxWidth()
            .padding(start = 10.dp, top = 2.dp, bottom = 0.dp, end = 10.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Column {
            Text(
                text = notify.extras.getString(Notification.EXTRA_TITLE, ""),
                style = MaterialTheme.typography.titleMedium
            )
            Text(text = notify.extras.getString(Notification.EXTRA_TEXT, ""))
        }

        largeIcon?.let {
            AsyncImage(
                model = it,
                contentDescription = "通知图标",
                modifier = Modifier.fillMaxHeight(),
                placeholder = painterResource(id = R.drawable.default_notify_icon),
                error = painterResource(id = R.drawable.default_notify_icon)
            )
        }
    }
}

额外优化建议

  • 给LazyColumn指定唯一itemKey,提升列表复用效率:
LazyColumn {
    items(notifications, key = { it.id }) { notify ->
        ListElement(notify = notify)
    }
}
  • 对加载后的Bitmap进行缩放,避免大图片占用过多内存

内容的提问来源于stack exchange,提问作者KiYugadgeter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:17:18