diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java index c2e3023d0269..89a747246c31 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -950,7 +950,7 @@ private ThemedReactContext getReactContextForView(int reactTag) { public void sendAccessibilityEvent(int tag, int eventType) { View view = mTagsToViews.get(tag); if (view == null) { - throw new JSApplicationIllegalArgumentException("Could not find view with tag " + tag); + throw new RetryableMountingLayerException("Could not find view with tag " + tag); } view.sendAccessibilityEvent(eventType); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index 93ccb0a9fbf9..de3ca5db4f18 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -591,7 +591,18 @@ private SendAccessibilityEvent(int tag, int eventType) { @Override public void execute() { - mNativeViewHierarchyManager.sendAccessibilityEvent(mTag, mEventType); + try { + mNativeViewHierarchyManager.sendAccessibilityEvent(mTag, mEventType); + } catch (RetryableMountingLayerException e) { + // Accessibility events are similar to commands in that they're imperative + // calls from JS, disconnected from the commit lifecycle, and therefore + // inherently unpredictable and dangerous. If we encounter a "retryable" + // error, that is, a known category of errors that this is likely to hit + // due to race conditions (like the view disappearing after the event is + // queued and before it executes), we log a soft exception and continue along. + // Other categories of errors will still cause a hard crash. + ReactSoftExceptionLogger.logSoftException(TAG, e); + } } } diff --git a/packages/rn-tester/android/app/gradle.properties b/packages/rn-tester/android/app/gradle.properties index 838ca18c4c1c..b6ce28ca151c 100644 --- a/packages/rn-tester/android/app/gradle.properties +++ b/packages/rn-tester/android/app/gradle.properties @@ -13,6 +13,6 @@ android.enableJetifier=true FLIPPER_VERSION=0.182.0 # RN-Tester is building with NewArch always enabled -newArchEnabled=true +newArchEnabled=false # RN-Tester is running with Hermes enabled and filtering variants with enableHermesOnlyInVariants hermesEnabled=true diff --git a/packages/rn-tester/js/CrashButton.js b/packages/rn-tester/js/CrashButton.js new file mode 100644 index 000000000000..756d0e02ee4f --- /dev/null +++ b/packages/rn-tester/js/CrashButton.js @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { Button, Platform, View } from 'react-native'; +import { setAccessibilityFocus } from './setAccessibilityFocus'; + +const isAndroid = Platform.OS === 'android'; +const timeoutMs = 250; + +export function CrashButton({ onPress }) { + const viewRef = React.useRef(); + + const handlePress = () => { + setAccessibilityFocus(viewRef.current, timeoutMs); + onPress(); + }; + + return ( + +