-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
Fatal Exception: java.lang.RuntimeException Unable to pause activity {.MainActivity}: java.lang.NullPointerException: null cannot be cast to non-null type com.reactnativenavigation.NavigationActivity
Caused by java.lang.NullPointerException null cannot be cast to non-null type com.reactnativenavigation.NavigationActivity com.reactnativenavigation.react.NavigationTurboModule.activity (NavigationTurboModule.kt:284)
node_modules\react-native-navigation\lib\android\app\src\main\java\com\reactnativenavigation\react\NavigationTurboModule.kt
It is called in (NavigationTurboModule.kt), where the activity is either null or a reference to an activity that has not yet been connected to the screen.
ReactContext.currentActivity may return null in some cases β especially:
If the activity is just being created,
If the application is in splash mode,
If the lifecycle is transitioning quickly (e.g., very fast launch + pause),
Or if the system calls onPause during the animation.
Example of error resolution:
package com.reactnativenavigation.react
import android.util.Log
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.reactnativenavigation.NavigationActivity
import com.reactnativenavigation.NavigationApplication
import com.reactnativenavigation.options.LayoutFactory
import com.reactnativenavigation.options.Options
import com.reactnativenavigation.options.parsers.JSONParser
import com.reactnativenavigation.options.parsers.LayoutNodeParser
import com.reactnativenavigation.options.parsers.TypefaceLoader
import com.reactnativenavigation.react.events.EventEmitter
import com.reactnativenavigation.utils.LaunchArgsParser
import com.reactnativenavigation.utils.Now
import com.reactnativenavigation.utils.SystemUiUtils.getStatusBarHeight
import com.reactnativenavigation.utils.UiThread
import com.reactnativenavigation.utils.UiUtils
import com.reactnativenavigation.viewcontrollers.navigator.Navigator
import com.reactnativenavigation.viewcontrollers.viewcontroller.ViewController
import java.util.Objects
class NavigationTurboModule(
reactContext: ReactApplicationContext,
private val layoutFactory: LayoutFactory
) : NativeRNNTurboModuleSpec(reactContext) {
private val now = Now()
private val jsonParser: JSONParser = JSONParser()
private lateinit var eventEmitter: EventEmitter
private var currentNavigationActivity: NavigationActivity? = null
init {
reactContext.addLifecycleEventListener(object : LifecycleEventListenerAdapter() {
override fun onHostPause() {
super.onHostPause()
currentNavigationActivity = null
UiUtils.runOnMainThread {
navigator()?.onHostPause()
}
}
override fun onHostResume() {
currentNavigationActivity = reactApplicationContext.currentActivity as? NavigationActivity
eventEmitter = EventEmitter(reactContext)
val nav = navigator()
val act = activity()
if (nav != null && act != null) {
nav.setEventEmitter(eventEmitter)
layoutFactory.init(
act,
eventEmitter,
nav.getChildRegistry(),
(act.application as NavigationApplication).externalComponents
)
UiUtils.runOnMainThread { nav.onHostResume() }
}
}
})
}
override fun getTypedExportedConstants(): MutableMap<String, Any> {
val constants = mutableMapOf<String, Any>()
val act = activity()
constants[Constants.BACK_BUTTON_JS_KEY] = Constants.BACK_BUTTON_ID
constants[Constants.BOTTOM_TABS_HEIGHT_KEY] =
UiUtils.pxToDp(
reactApplicationContext,
UiUtils.getBottomTabsHeight(reactApplicationContext).toFloat()
).toDouble()
constants[Constants.STATUS_BAR_HEIGHT_KEY] =
act?.let { UiUtils.pxToDp(reactApplicationContext, getStatusBarHeight(it).toFloat()).toDouble() }
?: 0.0
constants[Constants.TOP_BAR_HEIGHT_KEY] = UiUtils.pxToDp(
reactApplicationContext,
UiUtils.getTopBarHeight(reactApplicationContext).toFloat()
).toDouble()
return constants
}
override fun setRoot(commandId: String, layout: ReadableMap, promise: Promise) {
Log.d("NavigationTurboModule", "setRoot ${Thread.currentThread()}")
val layoutTree = LayoutNodeParser.parse(
Objects.requireNonNull(
jsonParser.parse(layout).optJSONObject("root")
)
)
handle {
Log.d("NavigationTurboModule", "setRoot handle ${Thread.currentThread()}")
val viewController = layoutFactory.create(layoutTree)
navigator()?.setRoot(
viewController,
NativeCommandListener("setRoot", commandId, promise, eventEmitter, now)
)
}
}
override fun setDefaultOptions(options: ReadableMap?) {
handle {
val defaultOptions = parse(options)
layoutFactory.defaultOptions = defaultOptions
navigator()?.let { it.defaultOptions = defaultOptions }
}
}
override fun mergeOptions(componentId: String?, options: ReadableMap?) {
handle { navigator()?.mergeOptions(componentId, parse(options)) }
}
override fun push(
commandId: String,
componentId: String,
layout: ReadableMap,
promise: Promise
) {
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(layout))
handle {
val viewController = layoutFactory.create(layoutTree)
navigator()?.push(
componentId,
viewController,
NativeCommandListener("push", commandId, promise, eventEmitter, now)
)
}
}
override fun pop(
commandId: String,
componentId: String,
options: ReadableMap?,
promise: Promise
) {
handle {
navigator()?.pop(
componentId,
parse(options),
NativeCommandListener("pop", commandId, promise, eventEmitter, now)
)
}
}
override fun popTo(
commandId: String,
componentId: String,
options: ReadableMap?,
promise: Promise
) {
handle {
navigator()?.popTo(
componentId,
parse(options),
NativeCommandListener("popTo", commandId, promise, eventEmitter, now)
)
}
}
override fun popToRoot(
commandId: String,
componentId: String,
options: ReadableMap?,
promise: Promise
) {
handle {
navigator()?.popToRoot(
componentId,
parse(options),
NativeCommandListener("popToRoot", commandId, promise, eventEmitter, now)
)
}
}
override fun setStackRoot(
commandId: String,
componentId: String,
children: ReadableArray,
promise: Promise
) {
handle {
val _children = ArrayList<ViewController<*>>()
for (i in 0..<children.size()) {
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(children.getMap(i)))
_children.add(layoutFactory.create(layoutTree))
}
navigator()?.setStackRoot(
componentId,
_children,
NativeCommandListener("setStackRoot", commandId, promise, eventEmitter, now)
)
}
}
override fun showModal(commandId: String, layout: ReadableMap, promise: Promise) {
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(layout))
handle {
val viewController = layoutFactory.create(layoutTree)
navigator()?.showModal(
viewController,
NativeCommandListener("showModal", commandId, promise, eventEmitter, now)
)
}
}
override fun dismissModal(
commandId: String,
componentId: String,
options: ReadableMap?,
promise: Promise
) {
handle {
navigator()?.mergeOptions(componentId, parse(options))
navigator()?.dismissModal(
componentId,
NativeCommandListener("dismissModal", commandId, promise, eventEmitter, now)
)
}
}
override fun dismissAllModals(commandId: String, options: ReadableMap?, promise: Promise) {
handle {
navigator()?.dismissAllModals(
parse(options),
NativeCommandListener("dismissAllModals", commandId, promise, eventEmitter, now)
)
}
}
override fun showOverlay(commandId: String, layout: ReadableMap, promise: Promise) {
val layoutTree = LayoutNodeParser.parse(jsonParser.parse(layout))
handle {
val viewController = layoutFactory.create(layoutTree)
navigator()?.showOverlay(
viewController,
NativeCommandListener("showOverlay", commandId, promise, eventEmitter, now)
)
}
}
override fun dismissOverlay(commandId: String, componentId: String, promise: Promise) {
handle {
navigator()?.dismissOverlay(
componentId,
NativeCommandListener("dismissOverlay", commandId, promise, eventEmitter, now)
)
}
}
override fun dismissAllOverlays(commandId: String, promise: Promise) {
handle {
navigator()?.dismissAllOverlays(
NativeCommandListener(
"dismissAllOverlays",
commandId,
promise,
eventEmitter,
now
)
)
}
}
override fun getLaunchArgs(commandId: String, promise: Promise) {
val act = activity()
if (act != null) {
promise.resolve(LaunchArgsParser.parse(act))
} else {
promise.reject("ACTIVITY_NOT_AVAILABLE", "Activity not available")
}
}
private fun parse(mergeOptions: ReadableMap?): Options {
val ctx = reactApplicationContext
return if (mergeOptions == null) {
Options.EMPTY
} else {
val activity = activity()
val typefaceLoader = if (activity != null) {
TypefaceLoader(activity)
} else {
// Fallback to context if activity is not available
TypefaceLoader(ctx)
}
Options.parse(
ctx,
typefaceLoader,
jsonParser.parse(mergeOptions)
)
}
}
private fun navigator(): Navigator? {
return activity()?.navigator
}
private fun handle(task: Runnable) {
UiThread.post {
val activity = activity()
if (activity != null && !activity.isFinishing) {
task.run()
} else {
Log.w("NavigationTurboModule", "Activity not available or finishing, skipping task")
}
}
}
private fun activity(): NavigationActivity? {
val current = reactApplicationContext.currentActivity as? NavigationActivity
if (current != null && current != currentNavigationActivity) {
currentNavigationActivity = current
}
return currentNavigationActivity
}
companion object {
const val NAME = "RNNTurboModule"
}
}React Native Navigation version: 8.1.0
React Native version: 0.80.1
Has Fabric (React Native's new rendering system) enabled: (yes/no) YES