From 5eaafc2156af32e80b154a5d7ebe6b98776004d0 Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 9 Mar 2026 22:06:12 +0100 Subject: [PATCH 1/2] fix: prevent NullPointerException in RNMBXAtmosphere.addStyles() Replace non-null assertions (!!) on mReactStyle and mMap with safe early-return null checks to prevent crashes when addStyles() is called before these properties are initialized. --- .../styles/atmosphere/RNMBXAtmosphere.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/atmosphere/RNMBXAtmosphere.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/atmosphere/RNMBXAtmosphere.kt index 8a55b1fd6..bb449bf02 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/atmosphere/RNMBXAtmosphere.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/atmosphere/RNMBXAtmosphere.kt @@ -52,15 +52,14 @@ class RNMBXAtmosphere(context: Context?) : AbstractSourceConsumer(context) { } fun addStyles() { - mAtmosphere?.also { - RNMBXStyleFactory.setAtmosphereLayerStyle( - it, RNMBXStyle( - context, mReactStyle!!, - mMap!! - ) - ) - } ?: run { + val atmosphere = mAtmosphere ?: run { Logger.e("RNMBXAtmosphere", "mAtmosphere is null") + return } + val reactStyle = mReactStyle ?: return + val map = mMap ?: return + RNMBXStyleFactory.setAtmosphereLayerStyle( + atmosphere, RNMBXStyle(context, reactStyle, map) + ) } } \ No newline at end of file From 6f2f31df78d23e26d68d3cc54ef215d29666803a Mon Sep 17 00:00:00 2001 From: Marco Date: Sat, 14 Mar 2026 22:53:03 +0100 Subject: [PATCH 2/2] example: add small Atmosphere crash reproducer --- .../src/examples/V10/AtmosphereRaceRepro.tsx | 84 +++++++++++++++++++ example/src/examples/V10/index.js | 1 + 2 files changed, 85 insertions(+) create mode 100644 example/src/examples/V10/AtmosphereRaceRepro.tsx diff --git a/example/src/examples/V10/AtmosphereRaceRepro.tsx b/example/src/examples/V10/AtmosphereRaceRepro.tsx new file mode 100644 index 000000000..e51991ac1 --- /dev/null +++ b/example/src/examples/V10/AtmosphereRaceRepro.tsx @@ -0,0 +1,84 @@ +import { useMemo, useState } from 'react'; +import { Button, StyleSheet, Text, View } from 'react-native'; +import { Atmosphere, Camera, MapView } from '@rnmapbox/maps'; + +const STYLE_DARK = 'mapbox://styles/mapbox/dark-v11'; +const STYLE_STANDARD = 'mapbox://styles/mapbox/standard'; + +const styles = StyleSheet.create({ + map: { + flex: 1, + }, + controls: { + position: 'absolute', + top: 50, + left: 16, + right: 16, + gap: 8, + backgroundColor: 'rgba(0,0,0,0.35)', + borderRadius: 10, + padding: 10, + }, + text: { + color: 'white', + }, +}); + +const AtmosphereRaceRepro = () => { + const [styleURL, setStyleURL] = useState(STYLE_DARK); + const [showAtmosphere, setShowAtmosphere] = useState(true); + + const atmosphereStyle = useMemo(() => { + const isDark = styleURL === STYLE_DARK; + return { + color: isDark ? 'rgba(29,44,62,1)' : 'rgba(255,255,255,1)', + highColor: isDark ? 'rgba(11,11,25,1)' : 'rgba(255,255,255,1)', + spaceColor: isDark ? 'rgba(11,11,25,1)' : 'rgba(255,255,255,1)', + horizonBlend: 0.03, + starIntensity: isDark ? 0.6 : 0, + }; + }, [styleURL]); + + function flipStyle() { + setStyleURL((prev) => (prev === STYLE_DARK ? STYLE_STANDARD : STYLE_DARK)); + } + + function remountAtmosphere() { + setShowAtmosphere(false); + requestAnimationFrame(() => setShowAtmosphere(true)); + } + + return ( + + + + {showAtmosphere ? : null} + + + + + Repro: tap quickly to trigger Atmosphere mount/style race + +