From 90186cd9b71bc6ffb593448c9bb5a3df66b3a0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 21 Jun 2023 19:57:19 -0700 Subject: [PATCH] Add workaround for android API 33 ANR when inverting ScrollView (#37913) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: As explained in this issue: - https://github.com/facebook/react-native/issues/35350 starting from android API 33 there are severe performance issues when using `scaleY: -1` on a view, and its child view, which is what we are doing when inverting the `ScrollView` component (e.g. in `FlatList`). This PR adds a workaround. The workaround is to also scale on the X-Axis which causes a different transform matrix to be created, that doesn't cause the ANR (see the issue for details). However, when doing that the vertical scroll bar will be on the wrong side, thus we switch the position in the native code once we detect that the list is inverted. The goal of this PR is that react-native users can just use `` without running into any ANRs or the need to apply manual hot fixes 😄 ## Changelog: [ANDROID] [FIXED] - ANR when having an inverted `FlatList` on android API 33+ Pull Request resolved: https://github.com/facebook/react-native/pull/37913 Test Plan: - The change is minimal, and only affects android. - Run the RNTesterApp for android and confirm that in the flatlist example the inverted list is still working as expected. Reviewed By: rozele Differential Revision: D46871197 Pulled By: NickGerleman fbshipit-source-id: 872a2ce5313f16998f0e4d2804d61e4d8dca7bfd --- .../views/scroll/ReactScrollViewManager.java | 18 ++++++++++++++++++ .../components/scrollview/ScrollViewProps.cpp | 15 +++++++++++++-- .../components/scrollview/ScrollViewProps.h | 1 + .../virtualized-lists/Lists/VirtualizedList.js | 11 ++++++++++- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java index 46e0ccf36c6..34e21553d39 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java @@ -379,4 +379,22 @@ public class ReactScrollViewManager extends ViewGroupManager public void setScrollEventThrottle(ReactScrollView view, int scrollEventThrottle) { view.setScrollEventThrottle(scrollEventThrottle); } + + @ReactProp(name = "inverted") + public void setInverted(ReactScrollView view, boolean inverted) { + // Usually when inverting the scroll view we are using scaleY: -1 on the list + // and on the parent container. HOWEVER, starting from android API 33 there is + // a bug that can cause an ANR due to that. Thus we are using different transform + // commands to circumvent the ANR. This however causes the vertical scrollbar to + // be on the wrong side. Thus we are moving it to the other side, when the list + // is inverted. + // See also: + // - https://github.com/facebook/react-native/issues/35350 + // - https://issuetracker.google.com/issues/287304310 + if (inverted) { + view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT); + } else { + view.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_DEFAULT); + } + } } diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp index fb70d8a6176..6222223a822 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.cpp @@ -319,7 +319,15 @@ ScrollViewProps::ScrollViewProps( rawProps, "scrollToOverflowEnabled", sourceProps.scrollToOverflowEnabled, - {})) {} + {})), + inverted( + CoreFeatures::enablePropIteratorSetter ? sourceProps.inverted + : convertRawProp( + context, + rawProps, + "inverted", + sourceProps.inverted, + {})) {} void ScrollViewProps::setProp( const PropsParserContext &context, @@ -368,6 +376,7 @@ void ScrollViewProps::setProp( RAW_SET_PROP_SWITCH_CASE_BASIC(snapToEnd); RAW_SET_PROP_SWITCH_CASE_BASIC(contentInsetAdjustmentBehavior); RAW_SET_PROP_SWITCH_CASE_BASIC(scrollToOverflowEnabled); + RAW_SET_PROP_SWITCH_CASE_BASIC(inverted); } } @@ -492,7 +501,9 @@ SharedDebugStringConvertibleList ScrollViewProps::getDebugProps() const { debugStringConvertibleItem( "snapToStart", snapToStart, defaultScrollViewProps.snapToStart), debugStringConvertibleItem( - "snapToEnd", snapToEnd, defaultScrollViewProps.snapToEnd)}; + "snapToEnd", snapToEnd, defaultScrollViewProps.snapToEnd), + debugStringConvertibleItem( + "inverted", inverted, defaultScrollViewProps.inverted)}; } #endif diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h index dea44da3af5..a734c14d568 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewProps.h @@ -68,6 +68,7 @@ class ScrollViewProps final : public ViewProps { ContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior{ ContentInsetAdjustmentBehavior::Never}; bool scrollToOverflowEnabled{false}; + bool inverted{false}; #pragma mark - DebugStringConvertible diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 0c078e08595..30f456d52b4 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -14,6 +14,7 @@ import type { LayoutEvent, ScrollEvent, } from 'react-native/Libraries/Types/CoreEventTypes'; +import Platform from 'react-native/Libraries/Utilities/Platform'; import type {ViewToken} from './ViewabilityHelper'; import type { Item, @@ -1969,7 +1970,15 @@ class VirtualizedList extends StateSafePureComponent { const styles = StyleSheet.create({ verticallyInverted: { - transform: [{scaleY: -1}], + transform: + // Android 13 Bug Workaround: + // On Android, we need to invert both axes to mitigate a native bug + // that could lead to ANRs. + // Simply using scaleY: -1 leads to the application of scaleY and + // rotationX natively, resulting in the ANR. + // For more information, refer to the following Android tracking issue: + // https://issuetracker.google.com/issues/287304310 + Platform.OS === 'android' ? [{scale: -1}] : [{scaleY: -1}], }, horizontallyInverted: { transform: [{scaleX: -1}],