Skip to content

Comments

fix(android): Fix Activity memory leak in ScreenDummyLayoutHelper due to unreleased view references and missing lifecycle cleanup#3638

Open
l2hyunwoo wants to merge 1 commit intosoftware-mansion:mainfrom
l2hyunwoo:feature/#3636
Open

fix(android): Fix Activity memory leak in ScreenDummyLayoutHelper due to unreleased view references and missing lifecycle cleanup#3638
l2hyunwoo wants to merge 1 commit intosoftware-mansion:mainfrom
l2hyunwoo:feature/#3636

Conversation

@l2hyunwoo
Copy link

Description

close #3636

Fix Activity memory leak in ScreenDummyLayoutHelper caused by unreleased view references (CoordinatorLayout, AppBarLayout, Toolbar, View) when the host Activity is destroyed.

The helper creates dummy views using Activity-scoped Context to measure header heights, but never releases these references on Activity destruction. This creates a strong reference chain (ScreenDummyLayoutHelper → Views → ContextThemeWrapper → Activity) that prevents GC of destroyed Activities.

The leak is caused by a combination of:

  1. lateinit var fields that cannot be set to null
  2. LifecycleEventListener conditionally registered (skipped when init succeeds in constructor)
  3. Listener prematurely removed in onHostResume after successful init
  4. onHostDestroy only removing the listener without releasing view references
  5. No thread safety between computeDummyLayout and potential cleanup

Changes

  • Changed view fields from lateinit var to nullable var? = null to allow reference release
  • Always register LifecycleEventListener in constructor (regardless of init success)
  • Keep listener registered across Activity lifecycle (never remove it) to ensure onHostDestroy is always called
  • Release all view references, reset cache, and reset isLayoutInitialized in onHostDestroy to break the reference chain and allow re-initialization on next onHostResume
  • Wrap computeDummyLayout in synchronized(this) block with null-safe local variables, returning 0.0f as fallback when views are already cleaned up
  • Synchronize onHostDestroy cleanup on the same monitor to prevent race conditions

Test plan

  1. Launch a React Native app with native stack navigation using react-native-screens
  2. Navigate through screens to trigger ScreenDummyLayoutHelper initialization
  3. Trigger Activity recreation (dev reload, configuration change, or process restoration)
  4. Use Android Studio Memory Profiler or LeakCanary to verify the previous Activity is no longer retained
  5. Repeat to confirm no leak accumulation across multiple Activity recreations
  6. Verify header height computation still works correctly after Activity recreation (headers render with correct dimensions)

Checklist

  • Included code example that can be used to test this change.
  • Updated / created local changelog entries in relevant test files.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

- Convert lateinit var to nullable var for view fields
- Always register LifecycleEventListener regardless of init result
- Null all view references and reset state in onHostDestroy
- Add synchronized block to prevent race between compute and cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] ScreenDummyLayoutHelper leaks Activity due to unreleased view references and missing lifecycle cleanup

1 participant