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

Android Jetpack Compose应用列表滚动卡顿,求优化方案

已安装应用列表滚动卡顿优化方案

核心问题分析

你的代码卡顿主要来自UI线程被耗时操作阻塞和不必要的重组触发,以下是具体优化点及修改后的代码:


优化点1:提前预加载应用图标和名称

info.loadIcon()和info.loadLabel()是耗时的IO/反射操作,直接在LazyColumn列表项中调用会导致滚动时UI线程阻塞。应提前在后台线程加载所有应用的图标和名称,再传递给Compose组件。

优化点2:精简remember的使用逻辑

列表项中的icon和label是静态数据(应用安装后不会实时变化),无需用mutableStateOf存储,只需用remember结合唯一key(如应用包名)缓存即可,避免多余的状态监听。

优化点3:替换自定义Painter为官方实现

官方提供了rememberDrawablePainter(),无需自行实现Painter逻辑,减少自定义代码带来的性能开销。

优化点4:简化复选框点击逻辑

移除外层Box的clickable,直接使用Checkbox的onCheckedChange处理状态切换,避免重复的点击事件处理和重组触发。

优化点5:使用不可变列表避免意外重组

将MutableList<ResolveInfo>改为List<ResolveInfo>,或用mutableStateListOf管理数据,确保Compose仅在数据真正变化时触发重组。


修改后的完整代码

// 先定义封装应用信息的数据类,提前预加载后传入
data class AppInfo(
    val packageName: String,
    val icon: Drawable,
    val label: String
)

@Composable
fun AppList(appInfoList: List<AppInfo>) {
    LazyColumn {
        // 绑定应用包名作为key,提升列表项复用效率
        items(items = appInfoList, key = { it.packageName }) { appInfo ->
            var isChecked by remember { mutableStateOf(false) }
            // 使用官方DrawablePainter加载图标
            val iconPainter = rememberDrawablePainter(appInfo.icon)

            Row(
                verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 8.dp)
            ) {
                Image(
                    painter = iconPainter,
                    contentDescription = appInfo.label,
                    contentScale = ContentScale.Fit,
                    modifier = Modifier
                        .size(48.dp) // 固定图标大小,避免布局重排
                        .padding(horizontal = 16.dp)
                )

                Text(
                    text = appInfo.label,
                    style = MaterialTheme.typography.bodyMedium,
                    modifier = Modifier.weight(1f)
                )

                Checkbox(
                    checked = isChecked,
                    onCheckedChange = { isChecked = it },
                    modifier = Modifier.padding(horizontal = 16.dp)
                )
            }
        }
    }
}

// 预加载应用信息示例(建议在ViewModel中用协程后台执行)
fun loadAppInfos(context: Context): List<AppInfo> {
    val packageManager = context.packageManager
    val intent = Intent(Intent.ACTION_MAIN, null).apply {
        addCategory(Intent.CATEGORY_LAUNCHER)
    }
    val resolveInfos = packageManager.queryIntentActivities(intent, 0)
    
    return resolveInfos.map { info ->
        AppInfo(
            packageName = info.activityInfo.packageName,
            icon = info.loadIcon(packageManager),
            label = info.loadLabel(packageManager).toString()
        )
    }
}

额外性能建议

  • 若应用数量极多,可考虑分页加载,避免一次性加载所有数据导致内存压力过大。
  • 图标可通过Glide或Coil的Compose扩展进行缓存和压缩,进一步降低内存占用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 15:31:58