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

Jetpack Compose中使用TileOverlays(热力图)时的内存泄漏问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 13:08:00