Skip to content

fix: prevent memory leaks in CoordinatorLayout and ViewPager2 - #16

Open
Lemkinator wants to merge 3 commits into
tribalfs:sesl-androidx-mainfrom
Lemkinator:fix/memory-leaks
Open

fix: prevent memory leaks in CoordinatorLayout and ViewPager2#16
Lemkinator wants to merge 3 commits into
tribalfs:sesl-androidx-mainfrom
Lemkinator:fix/memory-leaks

Conversation

@Lemkinator

@Lemkinator Lemkinator commented May 28, 2026

Copy link
Copy Markdown

Summary

CoordinatorLayout

  • mLastNestedScrollingChild changed from strong View ref to WeakReference<View> — prevents detached fragment views from being retained after back-stack navigation
  • Removed redundant assignment in onDetachedFromWindow (the subsequent onStopNestedScroll call already performs the same assignment)
  • All write sites wrap the value in new WeakReference<>(view) or null; the one read site in dispatchGenericMotionEvent resolves via .get() into a local before use

ViewPager2 / FragmentStateAdapter

  • FragmentStateAdapter(Fragment) registers mLifecycleObserver on the host fragment lifecycle (not view lifecycle). The observer captures FragmentMaxLifecycleEnforcermViewPager → child fragment views, retaining ~7 MB after the host fragment's view is destroyed
  • Fix: FragmentMaxLifecycleEnforcer now adds an OnAttachStateChangeListener to the ViewPager2 that nulls mViewPager on window-detach and restores it on reattach, breaking the retain chain without removing the lifecycle observer
  • updateFragmentMaxLifecycle() guarded with null check; unregister() also removes the attach state listener

Test plan

  • Navigate between fragments using CoordinatorLayout and verify LeakCanary reports no mLastNestedScrollingChild-related leaks
  • Navigate between fragments hosting a ViewPager2 with FragmentStateAdapter and verify LeakCanary reports no VP2 / child fragment view leaks
  • Confirm nested scroll / app bar expand-collapse and ViewPager2 paging behaviour are unchanged

Lemkinator and others added 3 commits May 26, 2026 21:48
… prevent memory leak

Samsung's SESL fork stores mLastNestedScrollingChild as a strong View reference and
never clears it when the scrolling child is removed from the window. This prevents
detached fragment view hierarchies from being GC'd after navigation.

Use WeakReference<View> instead, matching AOSP's original behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ent in onDetachedFromWindow

onStopNestedScroll already assigns mLastNestedScrollingChild; the preceding assignment
was redundant and caused an unnecessary WeakReference allocation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FragmentMaxLifecycleEnforcer registers mLifecycleObserver on the host
fragment lifecycle. The observer captures the enforcer, which holds
mViewPager, keeping the ViewPager2 and all child fragment views alive
after the host fragment's view is destroyed.

Fix: add an OnAttachStateChangeListener to the ViewPager2 that nulls
mViewPager on window-detach and restores it on reattach. Add a null
guard in updateFragmentMaxLifecycle() accordingly. unregister() also
removes the attach state listener.

The lifecycle observer is intentionally kept registered so it can still
catch up on lifecycle state changes if the ViewPager2 reattaches.
@Lemkinator Lemkinator changed the title coordinatorlayout: use WeakReference for mLastNestedScrollingChild to prevent memory leak fix: prevent memory leaks in CoordinatorLayout and ViewPager2 Jun 7, 2026
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.

1 participant