Jetpack Compose LazyColumn移除列表项后实例仍留存内存问题咨询
LazyColumn内存异常增长问题分析与解决方案
问题产生原因
- Compose 1.5.0以下版本存在LazyList内部缓存缺陷:LazyColumn内部会维护key到组合节点的映射表,用于快速复用重组节点,在列表数据源清空时,该映射表不会被主动清理,会持有最后一批渲染过的item实例的强引用,导致实例无法被GC回收。
- 相同key的实例更新触发的引用持有:两次更新列表时使用了相同的id作为item的key,Compose会判定为同一节点的内容更新,会在组合槽位中保留旧实例的引用,直到槽位被其他item复用或LazyColumn整体被销毁,列表清空时没有触发LazyColumn的整体销毁,因此旧引用会被留存。
- LazyColumn默认的预加载缓存机制:LazyColumn默认会缓存屏幕外最多2个item用于滑动预加载,同时内部维护的item复用池也会持有最多十几个最近使用过的item实例,频繁更新数据时复用池的实例也会持续累积。
解决方案
- 升级Compose版本:将Compose基础依赖(compose-foundation、compose-runtime等)同步升级到1.5.0及以上版本,该版本已经官方修复了LazyList清空后缓存未释放的问题,是优先级最高的解决方案。
- 为LazyColumn绑定数据源关联的key:如果暂时无法升级Compose版本,可以给LazyColumn添加
key属性,值关联数据源的整体标识,数据源全量更新或清空时自动触发LazyColumn整体重建,释放所有内部缓存,示例代码如下:
@Composable fun List(dataItems: List<TestDataBlock>) { LazyColumn( // 数据源整体变更时key变化,强制重建LazyColumn key = dataItems.hashCode(), modifier = Modifier.fillMaxSize() ) { itemsIndexed(items = dataItems, key = { index, item -> item.id } ) { rowIndex, rowItem -> drawElement(rowItem) } } }
- 全量更新数据时使用唯一key:如果业务场景允许,每次全量替换列表数据时生成全局唯一的item id,避免Compose将新实例判定为旧节点的更新,减少旧实例被持有的概率。
- 列表清空后手动触发GC:如果列表清空后页面会停留较长时间,可以在确认数据源置空后手动触发一次垃圾回收,清理无强引用的item实例,注意不要频繁调用该操作,避免造成性能损耗。
内容的提问来源于stack exchange,提问作者Qalterra
相关产品推荐
相关产品推荐

