OmniMap-Compose icon indicating copy to clipboard operation
OmniMap-Compose copied to clipboard

【闪退】MarkerManager.Collection.addMarker() 方法中的 NullPointerException 问题

Open Yorick-Ryu opened this issue 1 year ago • 2 comments

问题描述

在使用 MarkerManager.Collection.addMarker() 方法时,出现了 NullPointerException,错误信息为 "null cannot be cast to non-null type com.baidu.mapapi.map.Marker"。这个错误发生在 MarkerManager.kt 文件的第 96 行。最终造成App闪退。

复现步骤

在加载地图过程中偶现

错误堆栈

java.lang.NullPointerException: null cannot be cast to non-null type com.baidu.mapapi.map.Marker
    at com.melody.map.baidu_compose.utils.clustering.MarkerManager$Collection.addMarker(MarkerManager.kt:96)
    at com.melody.map.baidu_compose.utils.clustering.view.DefaultClusterRenderer$CreateMarkerTask.perform(DefaultClusterRenderer.kt:763)
    at com.melody.map.baidu_compose.utils.clustering.view.DefaultClusterRenderer$MarkerModifier.performNextTask(DefaultClusterRenderer.kt:566)
    at com.melody.map.baidu_compose.utils.clustering.view.DefaultClusterRenderer$MarkerModifier.handleMessage(DefaultClusterRenderer.kt:537)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7918)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

预期行为

addMarker() 方法应该成功地向地图添加一个标记,并返回创建的 Marker 对象。

实际行为

当尝试将 mMap.addOverlay(opts) 的结果转换为 Marker 类型时,方法抛出了 NullPointerException

可能的原因

问题可能出在 mMap.addOverlay(opts) 返回了 null 或非 Marker 类型的 Overlay,但代码直接将其强制转换为非空的 Marker 类型。

建议的修复方法

建议修改 addMarker() 方法以处理可能的空值和无效类型:

fun addMarker(opts: MarkerOptions?): Marker {
    if (opts == null) {
        throw IllegalArgumentException("MarkerOptions 不能为 null")
    }
    val overlay = mMap.addOverlay(opts)
    if (overlay !is Marker) {
        throw IllegalStateException("添加的 overlay 不是 Marker 类型")
    }
    val marker: Marker = overlay
    mMarkers.add(marker)
    mAllMarkers[marker] = this@Collection
    return marker
}

这个修改可以提供更好的错误处理和更明确的错误信息。

环境信息

  • 库版本:1.0.6
  • Android 版本:14
  • 设备型号:很多设备都有这个问题,已排除设备差异性

地图相关代码

@Composable
fun MapContent(
    modifier: Modifier,
    locationUiState: LocationUiState,
    cameraPositionState: CameraPositionState,
    center: LatLng,
    points: List<LatLng>,
    cpClusterItems: List<CPClusterItem>,
    deviceClusterItem: List<DeviceClusterItem>,
    ipadClusterItem: List<IpadClusterItem>,
    callPhone: (String) -> Unit,
    onClickRestore: (UpdateIpadStateRequest) -> Unit,
    onClickNavigation: (Int, LatLng) -> Unit,
) {
    var showInfoWindow by remember { mutableStateOf(false) }
    var selectedClusterItem by remember { mutableStateOf<ClusterItem?>(null) }
    LaunchedEffect(center) {
        cameraPositionState.position = BDCameraPosition(center, 18F, 0f, 0f)
    }
    BDMap(
        modifier = modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState,
        properties = locationUiState.mapProperties,
        uiSettings = locationUiState.mapUiSettings,
        locationSource = locationUiState.locationSource,
    ) {
        when (locationUiState.routePlanDataState) {
            is DrivingRouteDataState -> DrivingRouteOverlayContent(locationUiState.routePlanDataState)
            is BusRouteDataState -> BusRouteOverlayContent(locationUiState.routePlanDataState)
            is WalkRouteDataState -> WalkRouteOverlayContent(locationUiState.routePlanDataState)
            is RideRouteDataState -> RideRouteOverlayContent(locationUiState.routePlanDataState)
        }
        if (points.size >= 2) {
            Polyline(
                isThined = true,
                points = points,
                polylineColor = MaterialTheme.colorScheme.primary,
                width = 5
            )
        }
        if (cpClusterItems.isNotEmpty()) {
            ClusterOverlay(
                minClusterSize = 2,
                maxDistanceAtZoom = 30.dp,
                clusterItems = cpClusterItems,
                clusterColor = MaterialTheme.colorScheme.primary,
                onClusterItemClick = { clusterItem ->
                    selectedClusterItem = clusterItem
                    showInfoWindow = true
                    false
                },
                onClustersClick = { false },
                onClusterItemInfoWindow = { clusterItem ->
                    if (showInfoWindow && selectedClusterItem == clusterItem)
                        CPClusterItemInfoWindow(
                            clusterItem = clusterItem as CPClusterItem,
                            onClose = { showInfoWindow = false }
                        )
                }
            )
        }
        if (deviceClusterItem.isNotEmpty()) {
            ClusterOverlay(
                minClusterSize = 2,
                maxDistanceAtZoom = 30.dp,
                clusterItems = deviceClusterItem,
                clusterColor = MaterialTheme.colorScheme.primary,
                onClusterItemClick = { clusterItem ->
                    selectedClusterItem = clusterItem
                    showInfoWindow = true
                    false
                },
                onClustersClick = { false },
                onClusterItemInfoWindow = { clusterItem ->
                    if (showInfoWindow && selectedClusterItem == clusterItem)
                        DeviceClusterItemInfoWindow(
                            clusterItem = clusterItem as DeviceClusterItem,
                            callPhone = callPhone,
                            onClickNavigation = { queryType, toPoint ->
                                onClickNavigation(queryType, toPoint)
                                showInfoWindow = false
                            },
                            onClose = { showInfoWindow = false }
                        )
                }
            )
        }
        if (ipadClusterItem.isNotEmpty()) {
            ClusterOverlay(
                minClusterSize = 2,
                maxDistanceAtZoom = 30.dp,
                clusterItems = ipadClusterItem,
                clusterColor = MaterialTheme.colorScheme.primary,
                onClusterItemClick = { clusterItem ->
                    selectedClusterItem = clusterItem
                    showInfoWindow = true
                    false
                },
                onClustersClick = { false },
                onClusterItemInfoWindow = { clusterItem ->
                    if (showInfoWindow && selectedClusterItem == clusterItem)
                        IpadClusterItemInfoWindow(
                            clusterItem = clusterItem as IpadClusterItem,
                            onClickRestore = {
                                onClickRestore(it)
                                showInfoWindow = false
                            },
                            onClose = { showInfoWindow = false }
                        )
                }
            )
        }
    }
}

Yorick-Ryu avatar Sep 20 '24 02:09 Yorick-Ryu

问题描述

在使用 MarkerManager.Collection.addMarker() 方法时,出现了 NullPointerException,错误信息为 "null cannot be cast to non-null type com.baidu.mapapi.map.Marker"。这个错误发生在 MarkerManager.kt 文件的第 96 行。最终造成App闪退。

复现步骤

在加载地图过程中偶现

错误堆栈

java.lang.NullPointerException: null cannot be cast to non-null type com.baidu.mapapi.map.Marker
    at com.melody.map.baidu_compose.utils.clustering.MarkerManager$Collection.addMarker(MarkerManager.kt:96)
    at com.melody.map.baidu_compose.utils.clustering.view.DefaultClusterRenderer$CreateMarkerTask.perform(DefaultClusterRenderer.kt:763)
    at com.melody.map.baidu_compose.utils.clustering.view.DefaultClusterRenderer$MarkerModifier.performNextTask(DefaultClusterRenderer.kt:566)
    at com.melody.map.baidu_compose.utils.clustering.view.DefaultClusterRenderer$MarkerModifier.handleMessage(DefaultClusterRenderer.kt:537)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7918)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

预期行为

addMarker() 方法应该成功地向地图添加一个标记,并返回创建的 Marker 对象。

实际行为

当尝试将 mMap.addOverlay(opts) 的结果转换为 Marker 类型时,方法抛出了 NullPointerException

可能的原因

问题可能出在 mMap.addOverlay(opts) 返回了 null 或非 Marker 类型的 Overlay,但代码直接将其强制转换为非空的 Marker 类型。

建议的修复方法

建议修改 addMarker() 方法以处理可能的空值和无效类型:

fun addMarker(opts: MarkerOptions?): Marker {
    if (opts == null) {
        throw IllegalArgumentException("MarkerOptions 不能为 null")
    }
    val overlay = mMap.addOverlay(opts)
    if (overlay !is Marker) {
        throw IllegalStateException("添加的 overlay 不是 Marker 类型")
    }
    val marker: Marker = overlay
    mMarkers.add(marker)
    mAllMarkers[marker] = this@Collection
    return marker
}

这个修改可以提供更好的错误处理和更明确的错误信息。

环境信息

  • 库版本:1.0.6
  • Android 版本:14
  • 设备型号:很多设备都有这个问题,已排除设备差异性

地图相关代码

@Composable
fun MapContent(
    modifier: Modifier,
    locationUiState: LocationUiState,
    cameraPositionState: CameraPositionState,
    center: LatLng,
    points: List<LatLng>,
    cpClusterItems: List<CPClusterItem>,
    deviceClusterItem: List<DeviceClusterItem>,
    ipadClusterItem: List<IpadClusterItem>,
    callPhone: (String) -> Unit,
    onClickRestore: (UpdateIpadStateRequest) -> Unit,
    onClickNavigation: (Int, LatLng) -> Unit,
) {
    var showInfoWindow by remember { mutableStateOf(false) }
    var selectedClusterItem by remember { mutableStateOf<ClusterItem?>(null) }
    LaunchedEffect(center) {
        cameraPositionState.position = BDCameraPosition(center, 18F, 0f, 0f)
    }
    BDMap(
        modifier = modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState,
        properties = locationUiState.mapProperties,
        uiSettings = locationUiState.mapUiSettings,
        locationSource = locationUiState.locationSource,
    ) {
        when (locationUiState.routePlanDataState) {
            is DrivingRouteDataState -> DrivingRouteOverlayContent(locationUiState.routePlanDataState)
            is BusRouteDataState -> BusRouteOverlayContent(locationUiState.routePlanDataState)
            is WalkRouteDataState -> WalkRouteOverlayContent(locationUiState.routePlanDataState)
            is RideRouteDataState -> RideRouteOverlayContent(locationUiState.routePlanDataState)
        }
        if (points.size >= 2) {
            Polyline(
                isThined = true,
                points = points,
                polylineColor = MaterialTheme.colorScheme.primary,
                width = 5
            )
        }
        if (cpClusterItems.isNotEmpty()) {
            ClusterOverlay(
                minClusterSize = 2,
                maxDistanceAtZoom = 30.dp,
                clusterItems = cpClusterItems,
                clusterColor = MaterialTheme.colorScheme.primary,
                onClusterItemClick = { clusterItem ->
                    selectedClusterItem = clusterItem
                    showInfoWindow = true
                    false
                },
                onClustersClick = { false },
                onClusterItemInfoWindow = { clusterItem ->
                    if (showInfoWindow && selectedClusterItem == clusterItem)
                        CPClusterItemInfoWindow(
                            clusterItem = clusterItem as CPClusterItem,
                            onClose = { showInfoWindow = false }
                        )
                }
            )
        }
        if (deviceClusterItem.isNotEmpty()) {
            ClusterOverlay(
                minClusterSize = 2,
                maxDistanceAtZoom = 30.dp,
                clusterItems = deviceClusterItem,
                clusterColor = MaterialTheme.colorScheme.primary,
                onClusterItemClick = { clusterItem ->
                    selectedClusterItem = clusterItem
                    showInfoWindow = true
                    false
                },
                onClustersClick = { false },
                onClusterItemInfoWindow = { clusterItem ->
                    if (showInfoWindow && selectedClusterItem == clusterItem)
                        DeviceClusterItemInfoWindow(
                            clusterItem = clusterItem as DeviceClusterItem,
                            callPhone = callPhone,
                            onClickNavigation = { queryType, toPoint ->
                                onClickNavigation(queryType, toPoint)
                                showInfoWindow = false
                            },
                            onClose = { showInfoWindow = false }
                        )
                }
            )
        }
        if (ipadClusterItem.isNotEmpty()) {
            ClusterOverlay(
                minClusterSize = 2,
                maxDistanceAtZoom = 30.dp,
                clusterItems = ipadClusterItem,
                clusterColor = MaterialTheme.colorScheme.primary,
                onClusterItemClick = { clusterItem ->
                    selectedClusterItem = clusterItem
                    showInfoWindow = true
                    false
                },
                onClustersClick = { false },
                onClusterItemInfoWindow = { clusterItem ->
                    if (showInfoWindow && selectedClusterItem == clusterItem)
                        IpadClusterItemInfoWindow(
                            clusterItem = clusterItem as IpadClusterItem,
                            onClickRestore = {
                                onClickRestore(it)
                                showInfoWindow = false
                            },
                            onClose = { showInfoWindow = false }
                        )
                }
            )
        }
    }
}

是用baidu-sample里面的示例吗?看这个可能是markerOptions构建出来的Marker百度返回为空,markerOptions这里是初始化了新的,我晚上回去看看

TheMelody avatar Sep 20 '24 07:09 TheMelody

是的,我是仿照baidu-sample写的,感谢

Yorick-Ryu avatar Sep 20 '24 07:09 Yorick-Ryu

是的,我是仿照baidu-sample写的,感谢

已更1.0.7版本

TheMelody avatar Oct 16 '24 13:10 TheMelody

Thanks

Yorick-Ryu avatar Oct 17 '24 03:10 Yorick-Ryu