fix(android): Fix Activity memory leak in ScreenDummyLayoutHelper due to unreleased view references and missing lifecycle cleanup#3638
Conversation
- 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
|
Hi @l2hyunwoo , I hope you don't mind if I add a few additional changes to this PR to clean things up a bit. Also, based on the research I've done, I'm wondering if it might be better to rely on |
There was a problem hiding this comment.
Pull request overview
Fixes an Activity memory leak in ScreenDummyLayoutHelper where dummy views held strong references to destroyed Activities. The fix makes view fields nullable and adds cleanup via Application.ActivityLifecycleCallbacks.
Changes:
- Changed view fields from
lateinit varto nullablevar? = nulland addedcleanUpViews()to null them out on Activity destruction - Added
Application.ActivityLifecycleCallbacksto detect Activity destruction and trigger cleanup - Added a test reproduction file (
Test3636.tsx) for the memory leak scenario
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt |
Core fix: nullable view fields, lifecycle callback registration, cleanup logic |
apps/src/tests/issue-tests/Test3636.tsx |
Test reproduction app for the memory leak |
apps/src/tests/issue-tests/index.ts |
Exports the new test |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
kkafar
left a comment
There was a problem hiding this comment.
Hey, thanks for the contribution. I've left few remarks & question. Let's answer them before landing these changes.
kkafar
left a comment
There was a problem hiding this comment.
Looks ok. Let's roll this out and get some feedback.
|
thank you guys for working hard on this one! @kkafar is there an ETA for this? (I’m right on one step before rolling out a big release so sorry for the question 😅) |
|
Thank you all for this! We just discovered this issue in our app and stumbled upon the PR. Delighted to see that it's merged already! We're still on RN 0.81.5 and |
|
The 4.25.0 release waits for us being ready with Tabs API (stabilisation). I plan to release it ASAP, but first planned release date was ~ 1 month ago -> I can only say: soon ™️ @mduleone is using a patch a feasible option for you? I don't plan to backport this issue to any older version. Seemingly it has been there (the bug) for a long long time (unfortunately), and it is no regression introduced in any recent version. |
|
@kkafar thank you for the update. There's something off with the nightlies though? I see the latest one was submitted on npm 10 days ago, April 1st.
edit: yup, all failing: https://github.com/software-mansion/react-native-screens/actions/workflows/npm-screens-publish-nightly.yml |
|
Indeed, I haven't noticed it during last week. Thanks. We'll sort it out on Monday. |
Description
Fix Activity memory leak in
ScreenDummyLayoutHelpercaused by unreleased view references (CoordinatorLayout,AppBarLayout,Toolbar,View) when the host Activity is destroyed.The helper created dummy views using Activity-scoped
Contextto measure header heights, but never released these references on Activity destruction. This created a strong reference chain (ScreenDummyLayoutHelper → Views → ContextThemeWrapper → Activity) that prevents GC of destroyed Activities.Closes: #3636
Changes
lateinit varto nullablevar? = nullto allow reference releaseLifecycleEventListenerin constructor (regardless of init success)onHostDestroyis always calledisLayoutInitializedinonHostDestroyto break the reference chain and allow re-initialization on nextonHostResumecomputeDummyLayoutinsynchronized(this)block with null-safe local variables, returning0.0fas fallback when views are already cleaned uponHostDestroycleanup on the same monitor to prevent race conditionsScreenshots / GIFs
Before
Screen.Recording.2026-03-12.at.14.48.04.mov
After
Screen.Recording.2026-03-12.at.14.49.34.mov
Test plan
react-native-screensScreenDummyLayoutHelperinitializationChecklist