Android crash when navigating back from screen with MapView component
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 45/100
- Issue 类型
- 缺陷
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- android, react-native, typescript
- 领域
- mobile
调研方向
Reproduce the crash by opening the screen containing CustomSmallMap and triggering router.back() on Android. Start with the detachment path reported at MapView.java:341 and the Google Maps onSaveInstanceState stack; done means navigating back unmounts the MapView without a crash or logged exception.
由索引模型根据 Issue 内容生成。
描述
Summary
The app crashes on Android when I navigate back from a screen that contains a CustomSmallMap component. The crash occurs only on Android and is related to the Google Maps MapView.
Reproducible sample code
import { MapStyle } from '@/src/themePaper/MapStyles';
import React, { useEffect, useState } from 'react';
import { Keyboard, Pressable, StyleSheet, View } from 'react-native';
import MapView, { Marker, Region } from 'react-native-maps';
import { Icon, Text, useTheme } from 'react-native-paper';
import { spacing } from '../spacing';
import { typography } from '../typography';
import CustomIconAvatar from './CustomIconAvatar';
import { MapStyleLight } from '../MapStylesLight';
interface CustomSmallMapProps {
style?: object;
useInitialRegion?: boolean;
coordinate: {
latitude: number;
longitude: number;
};
onMarkerChange?: (newCoordinate: {
latitude: number;
longitude: number;
}) => void;
draggable?: boolean;
label?: string;
mapType?: 'standard' | 'satellite' | 'hybrid' | 'terrain' | 'none';
testID?: string;
staticMap?: boolean;
height?: number;
onPress: () => void;
}
const CustomSmallMap: React.FC<CustomSmallMapProps> = ({
style,
useInitialRegion,
coordinate,
onMarkerChange,
draggable = false,
label,
height = 144,
mapType = 'standard',
testID = 'map',
staticMap = false,
onPress,
}) => {
const { colors, dark } = useTheme();
const parseCoordinate = (coord: {
latitude: number | string;
longitude: number | string;
}) => ({
latitude: parseFloat(coord.latitude.toString()),
longitude: parseFloat(coord.longitude.toString()),
});
const [markerPosition, setMarkerPosition] = useState(parseCoordinate(coordinate));
const [mapRegion, setMapRegion] = useState<Region>({
...parseCoordinate(coordinate),
latitudeDelta: 0.0092,
longitudeDelta: 0.0092,
});
useEffect(() => {
const parsedCoordinate = parseCoordinate(coordinate);
setMarkerPosition(parsedCoordinate);
setMapRegion({
...parsedCoordinate,
latitudeDelta: 0.0032,
longitudeDelta: 0.0032,
});
Keyboard.dismiss();
}, [coordinate]);
const onMarkerDragEnd = (e: any) => {
const { latitude, longitude } = e.nativeEvent.coordinate;
const newCoordinate = { latitude, longitude };
setMarkerPosition(newCoordinate);
onMarkerChange?.(newCoordinate);
};
const onMapPress = (e: any) => {
if (!draggable) return;
const { latitude, longitude } = e.nativeEvent.coordinate;
const newCoordinate = { latitude, longitude };
setMarkerPosition(newCoordinate);
onMarkerChange?.(newCoordinate);
};
const onRegionChangeComplete = (region: Region) => {
setMapRegion(region);
};
return (
<View>
<Pressable onPress={onPress}>
{label && (
<Text
style={{
fontWeight: typography.fontVariants.system.medium,
fontSize: typography.fontSize.label.md,
marginBottom: spacing.xxs,
}}
>
<Icon source="map-marker" color={colors.primary} size={20} />
{label}
</Text>
)}
</Pressable>
<View style={[styles.container, { height }, style]} collapsable={false}>
<MapView
style={[styles.map, { height }]}
customMapStyle={!dark ? MapStyleLight : MapStyle}
mapType={mapType}
{...(useInitialRegion ? { initialRegion: mapRegion } : { region: mapRegion })}
onRegionChangeComplete={onRegionChangeComplete}
onPress={onMapPress}
accessible
accessibilityLabel={testID}
scrollEnabled={!staticMap}
zoomEnabled={!staticMap}
rotateEnabled={!staticMap}
pitchEnabled={!staticMap}
>
<Marker
flat
tracksViewChanges={false}
coordinate={markerPosition}
draggable={draggable}
onDragEnd={onMarkerDragEnd}
centerOffset={{ x: 0, y: -25 }}
>
<CustomIconAvatar
icon="map-marker"
color={colors.onPrimary}
size={35}
backgroundColor={colors.primary}
/>
</Marker>
</MapView>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
height: 140,
width: '100%',
borderRadius: 12,
overflow: 'hidden',
},
map: {
flex: 1,
width: '100%',
},
markerContainer: {
borderRadius: 100,
justifyContent: 'center',
alignItems: 'center',
},
markerInnerCircle: {
width: 40,
height: 40,
borderRadius: 100,
borderWidth: 2,
justifyContent: 'center',
alignItems: 'center',
},
});
export default CustomSmallMap;
Steps to reproduce
- Open the screen containing the CustomSmallMap component (the screen renders a MapView).
- Tap a button that triggers router.back() from Expo Router.
- The app crashes immediately on Android.
Expected result
- The app should navigate back to the previous screen without crashing.
- The CustomSmallMap component should unmount safely on Android.
- No errors or exceptions should appear in the log.
Actual result
ERROR Your app just crashed. See the error below.
java.lang.NullPointerException: Attempt to invoke interface method 'void com.google.maps.api.android.lib6.impl.co.C(android.os.Bundle)' on a null object reference
com.google.maps.api.android.lib6.impl.ef.k(:com.google.android.gms.policy_maps_core_dynamite@234910201@234910200049.587833741.587833741:3)
com.google.android.gms.maps.internal.s.bp(:com.google.android.gms.policy_maps_core_dynamite@234910201@234910200049.587833741.587833741:110)
m.bay.onTransact(:com.google.android.gms.policy_maps_core_dynamite@234910201@234910200049.587833741.587833741:21)
android.os.Binder.transact(Binder.java:667)
com.google.android.gms.internal.maps.zza.zzJ(com.google.android.gms:play-services-maps@@19.1.0:2)
com.google.android.gms.maps.internal.zzl.onSaveInstanceState(com.google.android.gms:play-services-maps@@19.1.0:3)
com.google.android.gms.maps.zzah.onSaveInstanceState(com.google.android.gms:play-services-maps@@19.1.0:3)
com.google.android.gms.dynamic.DeferredLifecycleHelper.onSaveInstanceState(com.google.android.gms:play-services-base@@18.4.0:1)
com.google.android.gms.maps.MapView.onSaveInstanceState(com.google.android.gms:play-services-maps@@19.1.0:1)
com.rnmaps.maps.MapView.onDetachedFromWindow(MapView.java:341)
android.view.View.dispatchDetachedFromWindow(View.java:18413)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3777)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3769)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.clearDisappearingChildren(ViewGroup.java:6852)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3771)
android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3769)
android.view.ViewGroup.endViewTransition(ViewGroup.java:6963)
com.swmansion.rnscreens.ScreenStack.endViewTransition(ScreenStack.kt:76)
androidx.fragment.app.DefaultSpecialEffectsController$AnimationEffect$onCommit$1.onAnimationEnd$lambda$0(DefaultSpecialEffectsController.kt:553)
androidx.fragment.app.DefaultSpecialEffectsController$AnimationEffect$onCommit$1.$r8$lambda$6fa8Ib9U-odOeQPVWaNv8aZmow0(Unknown Source:0)
androidx.fragment.app.DefaultSpecialEffectsController$AnimationEffect$onCommit$1$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
android.os.Handler.handleCallback(Handler.java:873)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loop(Looper.java:193)
android.app.ActivityThread.main(ActivityThread.java:6846)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:518)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
React Native Maps Version
^1.26.18
What platforms are you seeing the problem on?
Android
React Native Version
0.81.5
What version of Expo are you using?
SDK 53
Device(s)
Samsung
Additional information
Additional notes / possible cause:
- The crash seems related to MapView’s lifecycle when the view is being detached.
- It only happens on Android, not iOS.
- The component works fine when mounted but crashes on navigation back.
- 主要语言
- TypeScript
- 星标
- 16k
- 派生
- 5k
- 平均合并
- 17 小时 45 分钟
- 30 天内合并 PR
- 2
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
react-native-maps/react-native-maps 的其他 Issue
-
难度 1/5 1-3 小时 新手友好度 88/100
-
难度 2/5 1-3 小时 新手友好度 88/100
-
难度 1/5 1 小时以内 新手友好度 90/100
-
难度 2/5 1-3 小时 新手友好度 78/100
-
难度 2/5 1-3 小时 新手友好度 84/100
react-native-maps/react-native-maps#5987 · 1 条评论 ·
查看 react-native-maps/react-native-maps 的全部 Issue
相似的 Issue
-
blocklist removal
难度 2/5 1-3 小时 新手友好度 65/100
MetaMask/eth-phishing-detect#296544 ·
-
难度 2/5 1-3 小时 新手友好度 70/100
pastelsky/bundlephobia#1122 ·
-
难度 2/5 1-3 小时 新手友好度 70/100
-
category/development priority/P2 scope/file-operations scope/testing type/enhancement
难度 2/5 1-3 小时 新手友好度 75/100
-
难度 2/5 1-3 小时 新手友好度 75/100