react-native icon indicating copy to clipboard operation
react-native copied to clipboard

React Native throws Soft Exception when dispatching TouchEvents for custom native Android components

Open grahammendick opened this issue 1 year ago • 2 comments

Description

A custom native Android component can be made up of multiple native Views. For example, a custom TabBar renders a BottomNavigationView which internally renders many native views for the images and text that make up each tab. These native views aren't created by React Native, they're created by Android. So React Native isn’t in charge of what ids get assigned to these internal views.

The ReactRootView dispatches TouchEvents using hit-testing to work out which View triggered the touch. When the ReactEventEmitter tries to find the default event emitter it uses the id of this target View to decide whether to use the new or old architecture emitter. An even number is the new arch and an odd number is the old. But if the target is one of these internal Views created by Android then it could be either even or odd and React Native will throw if it finds the wrong one.

This breaks on both the old and the new architecture. But I've labelled this as a new architecture bug because that's when this odd/even architecture indicator idea came in.

I could workaround this problem by setting all the descendants to have the same ids as the container. But this isn’t ideal and I'm hoping there's a React Native fix for this.

Steps to reproduce

  1. Download the new architecture sample
  2. Set the theme to be "Theme.Material3.DayNight.NoActionBar"
  3. Run the sample from Android Studio
  4. Click the tabs around the edge of the ripple
  5. Notice the soft exception logged

React Native Version

0.75.2

Affected Platforms

Runtime - Android

Areas

Fabric - The New Renderer

Output of npx react-native info

System:
  OS: macOS 14.6.1
  CPU: (8) arm64 Apple M1 Pro
  Memory: 108.84 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.9.0
    path: /opt/homebrew/bin/node
  Yarn:
    version: 3.6.4
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.8.3
    path: /opt/homebrew/bin/npm
  Watchman:
    version: 2024.10.07.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.0
      - iOS 18.0
      - macOS 15.0
      - tvOS 18.0
      - visionOS 2.0
      - watchOS 11.0
  Android SDK:
    API Levels:
      - "29"
      - "30"
      - "31"
      - "32"
      - "33"
      - "34"
      - "35"
    Build Tools:
      - 29.0.2
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 32.0.0
      - 32.1.0
      - 33.0.0
      - 34.0.0
      - 35.0.0
    System Images:
      - android-30 | Intel x86 Atom_64
      - android-30 | Google APIs ARM 64 v8a
      - android-30 | Google Play ARM 64 v8a
      - android-32 | Google APIs ARM 64 v8a
      - android-33 | Google APIs ARM 64 v8a
      - android-33 | Google Play ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
      - android-35 | Google APIs ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2024.2 AI-242.21829.142.2421.12409432
  Xcode:
    version: 16.0/16A242d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.9
    path: /usr/bin/javac
  Ruby:
    version: 3.3.5
    path: /opt/homebrew/opt/ruby/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.3.1
    wanted: 18.3.1
  react-native:
    installed: 0.75.2
    wanted: 0.75.2
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

Unhandled SoftException
com.facebook.react.bridge.ReactNoCrashSoftException: Cannot find EventEmitter for receivedTouches: ReactTag[2131231014] UIManagerType[2] EventName[topTouchEnd]
	at com.facebook.react.uimanager.events.ReactEventEmitter.receiveTouches(ReactEventEmitter.java:102)
	at com.facebook.react.uimanager.events.TouchEvent.dispatchModern(TouchEvent.kt:124)
	at com.facebook.react.uimanager.events.EventDispatcherImpl$DispatchEventsRunnable.run(EventDispatcherImpl.java:376)
	at android.os.Handler.handleCallback(Handler.java:958)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
	at android.os.Looper.loopOnce(Looper.java:205)
	at android.os.Looper.loop(Looper.java:294)
	at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:235)
	at java.lang.Thread.run(Thread.java:1012)

Reproducer

https://github.com/grahammendick/navigation/tree/master/NavigationReactNative/sample/fabric

Screenshots and Videos

https://github.com/user-attachments/assets/a7d3608d-c23a-4830-99ae-665e4a2d6c56

grahammendick avatar Oct 26 '24 15:10 grahammendick

:warning: Add or Reformat Version Info
:information_source: We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2

react-native-bot avatar Oct 26 '24 15:10 react-native-bot

:warning: Add or Reformat Version Info
:information_source: We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2

react-native-bot avatar Oct 26 '24 15:10 react-native-bot

I could workaround this problem by setting all the descendants to have the same ids as the container. But this isn’t ideal and I'm hoping there's a React Native fix for this.

Quick question: What is the problem?

Is the SoftException (that means a debug log)? If so, you're right we should fix it. However that's not super critical as it's not breaking the functionality of the app in any form

cortinico avatar Nov 06 '24 20:11 cortinico

React Native is failing to raise TouchStart and TouchEnd events. I'm not sure if that's a problem. Maybe not for this BottomNavigationView example, but this will happen for any native Android component that's made up of multiple native Views. Also it's a SoftException now, but I'm concerned that React Native will change this to throw a hard exception in the future.

grahammendick avatar Nov 06 '24 20:11 grahammendick

Hey @cortinico, is it something that should be supported by React Native? I mean managing nodes that React Native does not know about (they are not In shadow tree)? I am just thinking about the proper solution to this and where it should be located.

coado avatar Nov 13 '24 11:11 coado

@coado The problem is that if a user taps a View that wasn't created by React Native then 50% of the time React Native won't be able to locate the EventEmitter. In these cases React Native fails to send TouchStart/End events and raises SoftExceptions.

React Native uses the tag of a View to decide which architecture it's on. This works for Views it creates because it uses odd numbers for one architecture and even numbers for the other. But it doesn't work for Views that it didn't create because then Android decides on the tag number.

The suggested fix is to remove this dependence on the View's tag number for determining the architecture.

grahammendick avatar Nov 27 '24 17:11 grahammendick

Hey @grahammendick, just to clarify, is it an issue with old architecture only? The exception seems to not be visible on Fabric. You can see that in getUIManagerType, the uiManagerType is mainly determined by surfaceId, which should technically be always presented. I can see that Fabric wasn't enabled previously in the repro here.

coado avatar Dec 03 '24 12:12 coado

@coado Oh, my bad, that's a great spot! I forgot to retest after I realised Fabric wasn't enabled. I've retested now and it doesn't happen on Fabric. I'm happy to close. Thank you 🙏

grahammendick avatar Dec 03 '24 16:12 grahammendick