【闪退】MarkerManager.Collection.addMarker() 方法中的 NullPointerException 问题
问题描述
在使用 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 }
)
}
)
}
}
}
问题描述
在使用
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这里是初始化了新的,我晚上回去看看
是的,我是仿照baidu-sample写的,感谢
是的,我是仿照baidu-sample写的,感谢
已更1.0.7版本
Thanks