Jetpack Compose中使用TileOverlays(热力图)时的内存泄漏问题
兄弟,我之前在Jetpack Compose里用Google Maps的热力图TileOverlay时,也碰到过一模一样的内存泄漏问题,折腾了好一阵才搞定!结合你的代码场景,给你梳理下问题根源和具体的解决办法。
为什么会出现泄漏?
咱们先搞明白核心问题:Compose有自己的生命周期体系,和传统View系统不一样。如果你只是在GoogleMapsApi里持有TileOverlay的引用,却没在Compose组件销毁时及时清理,或者不小心持有了Activity/Context的强引用,就会导致这些对象没法被GC回收,直接造成内存泄漏。
具体解决步骤
1. 给你的GoogleMapsApi类加上清理逻辑
首先得让这个类自己能管好Overlay的生命周期,提供明确的清理方法,不要让引用一直挂着:
class GoogleMapsApi(private val context: Context) { private var tileOverlay: TileOverlay? = null private var heatmapProvider: HeatmapTileProvider? = null // 新增:先清理旧的再添加新热力图,避免重复引用堆积 fun addHeatmapToMap(map: GoogleMap, heatmapData: List<LatLng>) { clearExistingOverlay() heatmapProvider = HeatmapTileProvider.Builder() .data(heatmapData) .build() tileOverlay = map.addTileOverlay( TileOverlayOptions().tileProvider(heatmapProvider) ) } // 核心清理方法:移除Overlay并置空所有引用 fun clearExistingOverlay() { tileOverlay?.remove() // 先从地图上移除Overlay实例 tileOverlay = null // 置空引用,让GC能正常回收 heatmapProvider = null } }
这里要特别注意:如果你的GoogleMapsApi之前持有了GoogleMap的强引用,赶紧去掉!GoogleMap是和MapView生命周期绑定的,持有它的引用会直接拖垮整个MapView没法被回收。
2. 在Compose里用DisposableEffect绑定生命周期
Compose里组件的销毁时机不能用传统的onDestroy,得用DisposableEffect来监听组件离开Composition的时机,在这时候调用清理方法:
@Composable fun HeatMapScreen() { val context = LocalContext.current // 用remember让GoogleMapsApi实例和Compose组件绑定,不要全局持有 val mapsApi = remember { GoogleMapsApi(context.applicationContext) } // 重点:用ApplicationContext,不要用Activity的Context! AndroidView( factory = { ctx -> MapView(ctx).apply { onCreate(Bundle()) getMapAsync { googleMap -> // 这里做地图初始化,比如设置缩放控件、初始位置 googleMap.uiSettings.isZoomControlsEnabled = true } } }, update = { mapView -> mapView.getMapAsync { googleMap -> // 假设你从ViewModel或者其他地方拿到了热力图数据 val heatmapData = listOf(LatLng(39.9042, 116.4074), LatLng(31.2304, 121.4737)) mapsApi.addHeatmapToMap(googleMap, heatmapData) } } ) // 关键:组件销毁时自动清理Overlay DisposableEffect(Unit) { onDispose { mapsApi.clearExistingOverlay() } } }
为什么要用ApplicationContext?因为Activity的Context是和页面生命周期绑定的,如果GoogleMapsApi持有它的强引用,就算页面销毁了,Activity也没法被GC回收,直接造成泄漏。用ApplicationContext就安全多了,它的生命周期和APP一致。
3. 额外检查点
- 如果你的热力图数据是从ViewModel里来的,记得在ViewModel的
onCleared()方法里也调用一次清理方法,双重保险 - 不要把
TileOverlay或者HeatmapTileProvider存到全局变量里,所有引用都要和组件生命周期绑定 - 如果是频繁更新热力图数据,每次更新前都要先清理旧的Overlay,不要直接覆盖引用
最后再啰嗦一句
我当时就是因为没注意用了Activity的Context,又没在DisposableEffect里清理,LeakCanary报了一堆泄漏,按照上面的步骤改完就全好了。你可以用LeakCanary再测一遍,确认泄漏问题解决。
内容来源于stack exchange

