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

Jetpack Compose中Google Maps点击信息窗口触发导航时应用冻结

解决Google Map InfoWindow点击导航时应用冻结的问题

我看你遇到的问题是点击地图InfoWindow触发导航时应用直接冻结,虽然其他Composable间的导航都正常,但这段代码里藏着阻塞主线程的坑,咱们一步步拆解解决:

核心问题分析

你当前代码里的同步地理编码操作是罪魁祸首:
coder.getFromLocationName()是一个同步方法,而且你在GoogleMap的Composable lambda里循环处理每个contact时都调用它——Composable的lambda是在UI主线程执行的,同步IO操作会直接卡住主线程,不仅地图加载卡顿,后续的导航操作也因为主线程被阻塞而无法正常响应。

解决方案

1. 用协程异步处理地理编码

把地理编码逻辑移到后台线程,避免阻塞UI。可以用LaunchedEffect配合Dispatchers.IO来实现:

GoogleMap(
    modifier = Modifier.fillMaxSize(),
    cameraPositionState = cameraPositionState,
    properties = mapProperties
) {
    // 用State保存所有经纬度,统一更新相机位置
    val allMarkerLatLngs = remember { mutableStateListOf<LatLng>() }
    val context = LocalContext.current

    testData.forEach { contact ->
        // 用remember+mutableState保存单个Marker的经纬度,响应式更新Marker位置
        var markerLatLng by remember(contact.address) { mutableStateOf(LatLng(0.0, 0.0)) }

        // 仅当地址变化时,异步执行地理编码
        LaunchedEffect(contact.address) {
            val resultLatLng = withContext(Dispatchers.IO) {
                val coder = Geocoder(context)
                try {
                    // 取第一个匹配的地址即可,无需循环所有结果
                    coder.getFromLocationName(contact.address, 1)
                        ?.firstOrNull()
                        ?.let { LatLng(it.latitude, it.longitude) }
                        ?: LatLng(0.0, 0.0)
                } catch (e: IOException) {
                    e.printStackTrace()
                    LatLng(0.0, 0.0)
                }
            }
            markerLatLng = resultLatLng
            if (markerLatLng != LatLng(0.0, 0.0)) {
                allMarkerLatLngs.add(markerLatLng)
            }
        }

        val markerState = rememberMarkerState(position = markerLatLng)
        Marker(
            state = markerState,
            title = contact.title,
            snippet = contact.description,
            onInfoWindowClick = {
                navController?.let { controller ->
                    // 确保导航操作在主线程执行(默认就是,但代码更严谨)
                    controller.currentBackStackEntry?.arguments?.putSerializable(
                        "contact_object", userJson
                    )
                    try {
                        controller.navigate(route = Routes.RecruitersInAreaDetail.route)
                    } catch (e: IllegalArgumentException) {
                        // 处理路由不存在等导航异常
                        e.printStackTrace()
                    }
                }
            }
        )
    }

    // 所有Marker经纬度收集完成后,统一更新相机位置
    LaunchedEffect(allMarkerLatLngs.size) {
        if (allMarkerLatLngs.isNotEmpty()) {
            val boundsBuilder = LatLngBounds.builder()
            allMarkerLatLngs.forEach { boundsBuilder.include(it) }
            cameraPositionState.move(
                CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(), 64)
            )
        }
    }
}

2. 关键优化点说明

  • 异步地理编码:用withContext(Dispatchers.IO)把地理编码移到后台线程,彻底避免阻塞UI主线程
  • 响应式状态管理:用mutableStateOf保存单个Marker的经纬度,地址变化时自动更新Marker位置
  • 批量相机更新:不再循环更新相机位置,而是等所有Marker经纬度收集完成后统一调整视角,减少不必要的UI刷新
  • 导航健壮性:增加navController的空安全检查和导航异常捕获,避免潜在崩溃

总结

应用冻结的本质是UI线程被同步IO操作阻塞,把地理编码逻辑异步化后,主线程就能正常处理导航等UI操作了。按照上面的代码调整后,应该就能解决冻结问题,同时地图加载和交互也会更流畅。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:22:32