From 92bbabbc86d3cb05cc8201742ffb8203f55c1c25 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Tue, 28 Apr 2026 08:34:49 -0700 Subject: [PATCH] Mark FPS listener in ScrollViews as final, avoid reset in initView (#56609) Summary: `mFpsListener` is injected via `ReactHorizontalScrollView`'s constructor and should live for the lifetime of the view. However, it was declared as a mutable field and nulled in `initView()`, which is called both during construction and when the view is recycled in a virtualized list. After recycling, `isScrollPerfLoggingEnabled()` returned false because `mFpsListener` was null, silently disabling FPS performance tracking for the rest of that view instance's lifecycle. The fix moves `mFpsListener` to a `final` field initialized in the constructor, and removes the `mFpsListener = null` assignment from `initView()`. Applied to `ReactScrollView`, `ReactHorizontalScrollView`, and `ReactNestedScrollView`. This is a safe change because both view managers are instantiated with a single instance of FPS listener, so recycled view instances will always reference the same FPS listener instance. Changelog: [Android][Fixed] - Fix FPS performance listener being cleared on ScrollView recycle Reviewed By: jehartzog Differential Revision: D102359747 --- .../react/views/scroll/ReactHorizontalScrollView.java | 3 +-- .../facebook/react/views/scroll/ReactNestedScrollView.java | 4 ++-- .../java/com/facebook/react/views/scroll/ReactScrollView.java | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 19bcde777231..fb8918fcd0d1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -101,6 +101,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private final VelocityHelper mVelocityHelper = new VelocityHelper(); private final Rect mTempRect = new Rect(); private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollX", 0, 0); + private final @Nullable FpsListener mFpsListener; private Rect mOverflowInset = new Rect(); private @Nullable VirtualViewContainerState mVirtualViewContainerState; @@ -113,7 +114,6 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private boolean mRemoveClippedSubviews; private boolean mScrollEnabled = true; private boolean mSendMomentumEvents; - private @Nullable FpsListener mFpsListener = null; private @Nullable String mScrollPerfTag; private @Nullable Drawable mEndBackground; private int mEndFillColor = Color.TRANSPARENT; @@ -177,7 +177,6 @@ private void initView() { mRemoveClippedSubviews = false; mScrollEnabled = true; mSendMomentumEvents = false; - mFpsListener = null; mScrollPerfTag = null; mEndBackground = null; mEndFillColor = Color.TRANSPARENT; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.java index 6cd0b927654e..a49ef0460083 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactNestedScrollView.java @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<41d03f4948562c0fae870a660e6e1eac>> + * @generated SignedSource<<2aa191314924bd0f969fba0e64b86142>> */ /** @@ -106,6 +106,7 @@ class ReactNestedScrollView extends NestedScrollView private final VelocityHelper mVelocityHelper = new VelocityHelper(); private final Rect mTempRect = new Rect(); private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollY", 0, 0); + private final @Nullable FpsListener mFpsListener; private Rect mOverflowInset; private @Nullable VirtualViewContainerState mVirtualViewContainerState; @@ -118,7 +119,6 @@ class ReactNestedScrollView extends NestedScrollView private boolean mRemoveClippedSubviews; private boolean mScrollEnabled; private boolean mSendMomentumEvents; - private @Nullable FpsListener mFpsListener; private @Nullable String mScrollPerfTag; private @Nullable Drawable mEndBackground; private int mEndFillColor; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 72e2db9c847a..d263aab7c5a4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -98,6 +98,7 @@ public class ReactScrollView extends ScrollView private final VelocityHelper mVelocityHelper = new VelocityHelper(); private final Rect mTempRect = new Rect(); private final ValueAnimator DEFAULT_FLING_ANIMATOR = ObjectAnimator.ofInt(this, "scrollY", 0, 0); + private final @Nullable FpsListener mFpsListener; private Rect mOverflowInset; private @Nullable VirtualViewContainerState mVirtualViewContainerState; @@ -110,7 +111,6 @@ public class ReactScrollView extends ScrollView private boolean mRemoveClippedSubviews; private boolean mScrollEnabled; private boolean mSendMomentumEvents; - private @Nullable FpsListener mFpsListener; private @Nullable String mScrollPerfTag; private @Nullable Drawable mEndBackground; private int mEndFillColor;