diff --git a/android/src/legacy/java/com/rive/RiveReactNativeView.kt b/android/src/legacy/java/com/rive/RiveReactNativeView.kt index 5b9244f2..7a3863d5 100644 --- a/android/src/legacy/java/com/rive/RiveReactNativeView.kt +++ b/android/src/legacy/java/com/rive/RiveReactNativeView.kt @@ -110,6 +110,9 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { if (willDispose) { riveAnimationView?.dispose() removeEventListeners() + // Post-dispose reads must resolve null, not the retained (released) + // instance; also stops pinning it for the view's remaining lifetime. + lastKnownViewModelInstance = null } super.onDetachedFromWindow() } @@ -181,7 +184,13 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { @Volatile private var lastKnownViewModelInstance: ViewModelInstance? = null + private val mainHandler = android.os.Handler(android.os.Looper.getMainLooper()) + private fun readViewModelInstanceOnMain(): ViewModelInstance? { + // A refresh posted before dispose() can run after it; reading the + // torn-down controller's stale list here would re-cache a released + // instance right after onDetachedFromWindow cleared it. + if (willDispose) return null val stateMachines = riveAnimationView?.controller?.stateMachines return if (!stateMachines.isNullOrEmpty()) { stateMachines.first().viewModelInstance @@ -195,7 +204,7 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { lastKnownViewModelInstance = readViewModelInstanceOnMain() return lastKnownViewModelInstance } - android.os.Handler(android.os.Looper.getMainLooper()).post { + mainHandler.post { lastKnownViewModelInstance = readViewModelInstanceOnMain() } return lastKnownViewModelInstance