Skip to content

Commit e55261b

Browse files
[android] harden AppStateModule initial state (facebook#57)
## Issue During app startup, react-native's `AppState.currentState` can incorrectly report `background` when the app is actually in the foreground. This happens because: 1. `AppStateModule` relies on `onHostResume` and `onHostPause` event subscriptions to track state 2. The initial state can incorrectly initialize to `APP_STATE_BACKGROUND` when `reactContext.lifecycleState === LifecycleState.BEFORE_CREATE` (which occurs before root view attachment) ## Solution This PR adds an `isAppForegroundedByMemoryState()` method that provides a more accurate initial state detection when the React context is in `BEFORE_CREATE` lifecycle state. ## Future Improvements While this is a quick workaround to address the immediate issue, potential future improvements: 1. Leveraging Android activity lifecycle to set initial state to `RESUMED` on `onCreate` 2. Mapping `LifecycleState.BEFORE_CREATE` to `unknown` instead of incorrectly mapping to `background`
1 parent 8f5bcca commit e55261b

File tree

1 file changed

+18
-0
lines changed
  • packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appstate

1 file changed

+18
-0
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
package com.facebook.react.modules.appstate
99

10+
import android.app.ActivityManager
11+
import com.facebook.common.logging.FLog
1012
import com.facebook.fbreact.specs.NativeAppStateSpec
1113
import com.facebook.react.bridge.Arguments
1214
import com.facebook.react.bridge.Callback
@@ -26,11 +28,27 @@ internal class AppStateModule(reactContext: ReactApplicationContext) :
2628
init {
2729
reactContext.addLifecycleEventListener(this)
2830
reactContext.addWindowFocusChangeListener(this)
31+
val isAppForegroundedByMemoryState = isAppForegroundedByMemoryState()
32+
// pasten: temporary debug log - remove after we validate with real users
33+
FLog.w("AppStateModule", "initial isAppForegroundedByMemoryState = $isAppForegroundedByMemoryState, " +
34+
"reactContext.lifecycleState = ${reactContext.lifecycleState}")
35+
2936
appState =
3037
if (reactContext.lifecycleState === LifecycleState.RESUMED) APP_STATE_ACTIVE
38+
// pasten: during cold start appState=APP_STATE_BACKGROUND while tha is actually in the foreground
39+
// best effort foreground detection when LifecycleState.BEFORE_CREATE (which is the initial state)
40+
else if (reactContext.lifecycleState === LifecycleState.BEFORE_CREATE && isAppForegroundedByMemoryState) {
41+
APP_STATE_ACTIVE
42+
} else if (isAppForegroundedByMemoryState) APP_STATE_ACTIVE
3143
else APP_STATE_BACKGROUND
3244
}
3345

46+
private fun isAppForegroundedByMemoryState(): Boolean {
47+
return ActivityManager.RunningAppProcessInfo().apply {
48+
ActivityManager.getMyMemoryState(this)
49+
}.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
50+
}
51+
3452
public override fun getTypedExportedConstants(): Map<String, Any> =
3553
mapOf(INITIAL_STATE to appState)
3654

0 commit comments

Comments
 (0)