Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Inverted FlatList + Text Input causing ANR on android 13 #35350

Closed
hannojg opened this issue Nov 15, 2022 · 35 comments
Closed

[Android] Inverted FlatList + Text Input causing ANR on android 13 #35350

hannojg opened this issue Nov 15, 2022 · 35 comments
Labels
Component: FlatList Component: TextInput Related to the TextInput component. Impact: Performance When the issue impacts the app running or build performance Needs: Triage 🔍 Platform: Android Android applications. Resolution: PR Submitted A pull request with a fix has been provided.

Comments

@hannojg
Copy link
Contributor

hannojg commented Nov 15, 2022

Description

When having a FlatList and a TextInput in one view and the FlatList has the inverted={true} prop set and the android device is on API 33 (Android 13) the app will freeze/die in an ANR.

Screen.Recording.2022-11-15.at.12.52.22.mov

Version

0.70.5

Output of npx react-native info

info Fetching system and libraries information...
System:
    OS: macOS 12.6
    CPU: (8) arm64 Apple M1 Pro
    Memory: 96.63 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.15.1 - ~/.nvm/versions/node/v16.15.1/bin/node
    Yarn: 1.22.19 - ~/.yarn/bin/yarn
    npm: 8.11.0 - ~/.nvm/versions/node/v16.15.1/bin/npm
    Watchman: Not Found
  Managers:
    CocoaPods: Not Found
  SDKs:
    iOS SDK:
      Platforms: DriverKit 21.4, iOS 16.0, macOS 12.3, tvOS 16.0, watchOS 9.0
    Android SDK:
      API Levels: 28, 29, 30, 31, 32, 33
      Build Tools: 29.0.2, 30.0.2, 30.0.3, 31.0.0, 32.0.0, 32.1.0, 33.0.0
      System Images: android-32 | Google APIs ARM 64 v8a, android-32 | Google APIs Intel x86 Atom_64, android-32 | Google Play ARM 64 v8a, android-33 | Google APIs ARM 64 v8a, android-33 | Google Play ARM 64 v8a
      Android NDK: 21.4.7075529
  IDEs:
    Android Studio: Chipmunk 2021.2.1 Patch 1 Chipmunk 2021.2.1 Patch 1
    Xcode: 14.0.1/14A400 - /usr/bin/xcodebuild
  Languages:
    Java: 17.0.5 - /Users/hannogodecke/.jenv/shims/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.1.0 => 18.1.0 
    react-native: 0.70.5 => 0.70.5 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Steps to reproduce

  • Clone this repository
  • Run yarn
  • Run yarn start
  • Run yarn android, make sure the app launches on a device with android API 33 (Android 13)
  • Open the app
  • Open the developer tools and enable the performance monitor
  • Focus the text input
  • Run the following in your terminal:
adb shell input text "Lorem\ ipsum\ dolor\ sit\ amet,\ consetetur\ sadipscing\ elitr,\ sed\ diam\ nonumy\ eirmod\ tempor\ invidunt\ ut\ labore\ et\ dolore\ magna\ aliquyam\ erat,\ sed\ diam\ voluptua.\ At\ vero\ eos\ et\ accusam\ et\ justo\ duo\ dolores\ et\ ea\ rebum.\ Stet\ clita\ kasd\ gubergren,\ no\ sea\ takimata\ sanctus\ est\ Lorem\ ipsum\ dolor\ sit\ amet.\ Lorem\ ipsum\ dolor\ sit\ amet,\ consetetur\ sadipscing\ elitr,\ sed\ diam\ nonumy\ eirmod\ tempor\ invidunt\ ut\ labore\ et\ dolore\ magna\ aliquyam\ erat,\ sed\ diam\ voluptua.\ At\ vero\ eos\ et\ accusam\ et\ justo\ duo\ dolores\ et\ ea\ rebum.\ Stet\ clita\ kasd\ gubergren,\ no\ sea\ takimata\ sanctus\ est\ Lorem\ ipsum\ dolor\ sit\ amet."
  • Run it repeatedly until the app freezes (for me it takes ~ 3 repetitions until the ANR happens. On a real Pixel 4 of a colleague the app almost freezes immediately).

Snack, code example, screenshot, or link to a repository

https://github.com/hannojg/RN-AndroidAPI33-TextInput-ANR

@rudenick0309
Copy link

rudenick0309 commented Nov 15, 2022

#35155
The similar problem seems to occur even with only 'TextInput'

@hannojg
Copy link
Contributor Author

hannojg commented Nov 16, 2022

Interestingly the TextInput works for me on the device but stops working once it's next to an inverted FlatList. So I am not entirely sure if it's related, but I will test the reproduction of the issue you mentioned as well!

@hannojg
Copy link
Contributor Author

hannojg commented Nov 16, 2022

Observations I made:

  • I just put a plain android EditText view instead of RN's TextInput and the issue still happens (so I assume it isn't the TextInput implementation)
  • I tried it with VirtualizedList and the following styles (to re-create the inverted effect), but the ANR still happens:
style={{
    transform: [{scaleY: -1}],
}}
contentContainerStyle={{
    transform: [{scaleY: -1}],
}}
  • I tried it with ScrollView (used by VirtualizedList) using the styles above, and the issue keeps happening as well
  • If I don't do the inverted styling (its the same way VirtualizedList does it) the ANR doesn't happen

Still looking for the place that's causing the issue 🔍

@JuanAlejandro
Copy link

JuanAlejandro commented Nov 16, 2022

@hannojg we had the same problem with our app. We are using the inverted FlatList in our chat screen. One time I wasn't typing, I was just receiving messages and the app froze. So I'm not 100% sure the TextInput is needed to repro. The workaround we implemented was to invert the list with styles instead of the prop. You can check the solution in this Github issue.

Ah! And also this issue was happening only for Pixel devices with Android 13. Since the first family of devices to get Android 13 was the Pixel, maybe that's why we only saw this issue in Pixel devices.

@hannojg
Copy link
Contributor Author

hannojg commented Nov 16, 2022

Wow thx for sharing this information, will give it a try!

What I found is, that you don't even need a list. You just need two views that have scaleY: 1 set:

     <View
        style={{
          flex: 1,
          backgroundColor: 'red',
          transform: [{scaleY: -1}],
        }}>
        <View
          style={{
            transform: [{scaleY: -1}],
          }}>
           {/* Some more children inside here that got inverted */}

However, I still need to input text constantly into a TextInput to cause the ANR 😅

@hannojg
Copy link
Contributor Author

hannojg commented Nov 16, 2022

Okay, the issue doesn't happen anymore if I also add scaleX: -1 👀 :

     <View
        style={{
          flex: 1,
          backgroundColor: 'red',
          transform: [{scaleX: -1}, {scaleY: -1}],
        }}>
        <View
          style={{
            transform: [{scaleX: -1}, {scaleY: -1}],
          }}>

Going to dig into the native code to see where the issue happens, maybe also a bug with yoga?

@rudenick0309
Copy link

@hannojg @JuanAlejandro sorry for telling this, this problem was occured at real device of galaxy22 with google play system version - 22.07.01, but, after updating google play system version to '22.10.01'. the problem was disappeared. ( i could not test my local, that device is our customer. )
so, i am looking for other galaxy22 for verifying. after results are out, i will write down.

hannojg added a commit to margelo/expensify-app-fork that referenced this issue Nov 17, 2022
hannojg added a commit to hannojg/react-native that referenced this issue Nov 17, 2022
This is a temporary addition to help circumvent a bug on android.
Long term we want to fix the root cause of the problem.

- RN issue: facebook#35350
- Expensify issue: Expensify/App#11321 (comment)
@hannojg
Copy link
Contributor Author

hannojg commented Nov 17, 2022

I just found the offending line that's causing the issue. If I remove the view.setRotationX line the issue stops happening.
At this point I am not sure whether this is a react native or an android bug. Will dig deeper:

private static void setTransformProperty(@NonNull View view, ReadableArray transforms) {
sMatrixDecompositionContext.reset();
TransformHelper.processTransform(transforms, sTransformDecompositionArray);
MatrixMathHelper.decomposeMatrix(sTransformDecompositionArray, sMatrixDecompositionContext);
view.setTranslationX(
PixelUtil.toPixelFromDIP(
sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.translation[0])));
view.setTranslationY(
PixelUtil.toPixelFromDIP(
sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.translation[1])));
view.setRotation(
sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.rotationDegrees[2]));
view.setRotationX(
sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.rotationDegrees[0]));
view.setRotationY(
sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.rotationDegrees[1]));
view.setScaleX(sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.scale[0]));
view.setScaleY(sanitizeFloatPropertyValue((float) sMatrixDecompositionContext.scale[1]));
double[] perspectiveArray = sMatrixDecompositionContext.perspective;
if (perspectiveArray.length > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
float invertedCameraDistance =
(float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX];
if (invertedCameraDistance == 0) {
// Default camera distance, before scale multiplier (1280)
invertedCameraDistance = 0.00078125f;
}
float cameraDistance = -1 / invertedCameraDistance;
float scale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
// The following converts the matrix's perspective to a camera distance
// such that the camera perspective looks the same on Android and iOS.
// The native Android implementation removed the screen density from the
// calculation, so squaring and a normalization value of
// sqrt(5) produces an exact replica with iOS.
// For more information, see https://github.com/facebook/react-native/pull/18302
float normalizedCameraDistance =
sanitizeFloatPropertyValue(
scale * scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER);
view.setCameraDistance(normalizedCameraDistance);
}
}

AndrewGable pushed a commit to Expensify/react-native that referenced this issue Nov 21, 2022
This is a temporary addition to help circumvent a bug on android.
Long term we want to fix the root cause of the problem.

- RN issue: facebook#35350
- Expensify issue: Expensify/App#11321 (comment)
@hannojg
Copy link
Contributor Author

hannojg commented Nov 22, 2022

So at this point, I was able to reproduce the issue with just a native android project (no react native involved). It's a bug in the android framework.

I tried to create an issue ticket on the AOSP, however, after creation, I get weird permission errors and the bug ticket just disappears.

Sorry for the ping, but maybe @cortinico you can help with opening a bug ticket for the android team (I recall you were an android engineer? 😅 )?

The bug report for the native android looks like this:

Setting scaleY: -1 and rotationX: 180 for two nested views causes an ANR on android API 33

On Android 13 we are noticing an ANR, when we have two views that have each:

scaleY: -1

rotationX: 180

set.

In the video, you can see a green view, the parent of a blue view (and the blue view has a lot of children (list)). Both views have the transformation properties set (creating an "inverted" effect).

We then have an EditText rendered underneath these views. When entering text the ANR happens. This is much easier to reproduce on a real device. We are reproducing with a Pixel 4 Android API 33 emulator. Here you need to enter text rapidly with the help of add shell input text to cause the ANR.

In the bottom right you can see a small FPS counter (using Choreographer), and you can see that the app starts to freeze at some point.

The full reproduction: https://github.com/hannojg/AndroidScaleYReproductionANR-

I also attached an adb bugreport (it's from an emulator, so no sensitive data)

The important code part to reproduce:

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val parentView = RelativeLayout(this)
        parentView.layoutParams = ViewGroup.LayoutParams(1000, 2000)
        parentView.setBackgroundColor(0xFF00FF00.toInt())
        parentView.scaleY = -1f
        parentView.rotationX = 180f

        // add a child view to the parentView which we invert as well:
        val childView = LinearLayout(this)
        childView.orientation = LinearLayout.VERTICAL
        childView.layoutParams = ViewGroup.LayoutParams(800, 1800)
        childView.setBackgroundColor(0xFF0000FF.toInt())
        childView.scaleY = -1f
        childView.rotationX = 180f

        parentView.addView(childView)

        // add 20 text views to the child view
        for (i in 0..19) {
            val textView = TextView(this)
            val layoutPrams = ViewGroup.MarginLayoutParams(200, 100)
            layoutPrams.topMargin = 10
            textView.layoutParams = layoutPrams
            textView.setBackgroundColor(0xFFD3D3D3.toInt())
            textView.gravity = Gravity.CENTER
            textView.text = "Text $i"

            childView.addView(textView)
        }

        binding.rootLayout.addView(parentView)
    }
}

Thanks in advance for checking!

android-reproduction.mov

@cortinico
Copy link
Contributor

Sorry for the ping, but maybe @cortinico you can help with opening a bug ticket for the android team (I recall you were an android engineer? 😅 )?

You can do so by yourself here:
https://issuetracker.google.com/issues

@cortinico
Copy link
Contributor

Closign also as it's a Android Bug apparently. Feel free to reopen if they send you here

@GuoXiaoyang
Copy link

@hannojg Hi, have you opened a bug ticket for the android team? Same issue here.

@hannojg
Copy link
Contributor Author

hannojg commented Nov 24, 2022

Yeah that's the point, opening a bug ticket at https://issuetracker.google.com/issues doesn't work for me and results in permission errors.
Does anyone of you have created an issue there and can help with that?

@GuoXiaoyang
Copy link

GuoXiaoyang commented Nov 24, 2022

Yeah that's the point, opening a bug ticket at https://issuetracker.google.com/issues doesn't work for me and results in permission errors. Does anyone of you have created an issue there and can help with that?

I will have a try. And I noticed that you have forked the React Native repo and added a verticalScrollbarPosition prop to FlatList to fix this issue as a workaround. But it's a little difficult for us to use a forked RN version😭.
I will try to find another solution if possible. Do you have any suggestions?

@hannojg
Copy link
Contributor Author

hannojg commented Nov 24, 2022

Yeah please try, you can copy paste the bug report I pasted in one of the above comments.
I always get this weird error after creation:

Screenshot 2022-11-24 at 10 30 11

Yeah, I was thinking about adding verticalScrollbarPosition prop to react native itself. Unfortunately, I haven't found any other workaround yet

@EmilScherdin
Copy link

@hannojg I believe I succeeded to add it with the correct info and at the right place (the issue tracker is a jungle 😅) https://issuetracker.google.com/issues/261572772

@hannojg
Copy link
Contributor Author

hannojg commented Dec 9, 2022

@EmilScherdin unfortunately not, I can't open that issue either:

Screenshot 2022-12-09 at 10 50 06

@MohammadAzimi
Copy link

The issue is closed. Then what is the workaround or any update in react native? Can someone help me please

lunaleaps pushed a commit that referenced this issue Aug 7, 2023
Summary:
This PR is a result of this PR, which got merged but then reverted:

- #37913

We are trying to implement a workaround for #35350, so react-native users on android API 33+ can use `<FlatList inverted={true} />` without running into ANRs.

This is the native part, where we add a new internal prop named `isInvertedVirtualizedList`, which can in a follow up change be used to achieve the final fix as proposed in #37913

However as NickGerleman pointed out, its important that we first ship the native change.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[ANDROID] [ADDED] - Native part of fixing ANR when having an inverted FlatList on android API 33+

Pull Request resolved: #38071

Test Plan:
- Check the RN tester app and see that scrollview is still working as expected
- Add the `isInvertedVirtualizedList` prop as test to a scrollview and see how the scrollbar will change position.

Reviewed By: rozele

Differential Revision: D47062200

Pulled By: NickGerleman

fbshipit-source-id: d20eebeec757d9aaeced8561f53556bbb4a492e4
lunaleaps pushed a commit that referenced this issue Aug 7, 2023
Summary:
This PR is a result of this PR, which got merged but then reverted:

- #37913

We are trying to implement a workaround for #35350, so react-native users on android API 33+ can use `<FlatList inverted={true} />` without running into ANRs.

As explained in the issue, 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, using the newly added `isInvertedVirtualizedList` prop.

This is a follow up PR to:

- #38071

⚠️ **Note:** [38071](#38071) needs to be merged and shipped first! Only then we can merge this PR.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[ANDROID] [FIXED] - ANR when having an inverted FlatList on android API 33+

Pull Request resolved: #38073

Test Plan:
- Check the RN tester app and see that scrollview is still working as expected
- Add the `internalAndroidApplyInvertedFix` prop as test to a scrollview and see how the scrollbar will change position.

Reviewed By: cortinico

Differential Revision: D47848063

Pulled By: NickGerleman

fbshipit-source-id: 4a6948a8b89f0b39f01b7a2d44dba740c53fabb3
Kudo pushed a commit to expo/react-native that referenced this issue Aug 21, 2023
…book#38071)

Summary:
This PR is a result of this PR, which got merged but then reverted:

- facebook#37913

We are trying to implement a workaround for facebook#35350, so react-native users on android API 33+ can use `<FlatList inverted={true} />` without running into ANRs.

This is the native part, where we add a new internal prop named `isInvertedVirtualizedList`, which can in a follow up change be used to achieve the final fix as proposed in facebook#37913

However as NickGerleman pointed out, its important that we first ship the native change.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[ANDROID] [ADDED] - Native part of fixing ANR when having an inverted FlatList on android API 33+

Pull Request resolved: facebook#38071

Test Plan:
- Check the RN tester app and see that scrollview is still working as expected
- Add the `isInvertedVirtualizedList` prop as test to a scrollview and see how the scrollbar will change position.

Reviewed By: rozele

Differential Revision: D47062200

Pulled By: NickGerleman

fbshipit-source-id: d20eebeec757d9aaeced8561f53556bbb4a492e4
@stelmakhivan
Copy link

@hannojg is this issue already fixed in react-native v0.72.4?
https://github.com/facebook/react-native/releases/tag/v0.72.4

@winterdouglas
Copy link

winterdouglas commented Oct 6, 2023

The android bug tracker seems to be working again: https://issuetracker.google.com/issues/287304310

Meanwhile I am preparing a workaround fix for within react native.

Any chances that your fix is also applied to 0.71? :) @hannojg

@roryabraham
Copy link

It looks like as of now it's only applied in 0.73: 3dd816c

Copy link

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label May 21, 2024
@phatmann
Copy link

Is this actually fixed?

@github-actions github-actions bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label May 22, 2024
@hannojg
Copy link
Contributor Author

hannojg commented Jun 17, 2024

ah yeah it is, we applied a workaround in react native:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: FlatList Component: TextInput Related to the TextInput component. Impact: Performance When the issue impacts the app running or build performance Needs: Triage 🔍 Platform: Android Android applications. Resolution: PR Submitted A pull request with a fix has been provided.
Projects
None yet
Development

No branches or pull requests

17 participants