Jetpack Compose中用AndroidView嵌入自定义View出现内存泄漏如何解决
排查方向&解决方案
1. 导航配置问题
使用Navigation Compose实现底部导航时,如果配置了saveState = true的状态保存逻辑,Navigation框架会默认保存旧目的地的Composable节点和关联的View实例用于状态恢复,不会主动销毁这部分实例,属于预期行为。如果不需要状态保存,可以在导航调用的popUpTo方法中关闭saveState参数,同时导航时不设置restoreState = true,即可让旧页面退出Composition时自动释放关联资源。
2. Compose版本已知Bug
低于1.3.0版本的Compose UI存在AndroidView引用未正确释放的已知问题,当Composable节点退出Composition时,AndroidView持有的View实例可能被Compose的渲染节点树意外持有导致无法回收,升级Compose UI版本到1.3.0及以上即可解决该问题。
3. 自定义View隐式持有引用
即便当前的自定义View只有基础的文本设置逻辑,如果后续在自定义View中添加了点击监听、广播注册、RxJava/Flow订阅、全局静态类引用等逻辑,没有在onDetachedFromWindow回调中做解绑、取消订阅操作,也会导致自定义View被长期持有无法回收。可以重写MyCustomView的onDetachedFromWindow方法,在该方法中完成所有资源释放操作:
override fun onDetachedFromWindow() { super.onDetachedFromWindow() // 此处添加解绑监听器、取消订阅、清空引用等操作 }
4. 内存分析操作误差
在Android Studio中抓取内存快照前,需要手动点击Profiler中的Force Garbage Collection按钮触发多次GC,否则会存在大量还没被系统回收的临时对象被误判为内存泄漏。
临时修复方案
如果需要强制让AndroidView在页面退出时释放引用,可以给AndroidView添加唯一key参数,或者在页面的DisposableEffect回调中手动清理引用:
@Composable fun ScreenTwo() { Box(modifier = Modifier.fillMaxSize()) { AndroidView( modifier = Modifier.fillMaxSize(), factory = { context -> MyCustomView(context) }, ) } DisposableEffect(Unit) { onDispose { // 若有外部持有MyCustomView的引用,可在此处手动置空 } } }
内容的提问来源于stack exchange,提问作者Artem Garkusha

