From 9c165730590686edb5bc05a5249ce3a422a05527 Mon Sep 17 00:00:00 2001 From: Harry Yu Date: Mon, 9 May 2022 17:09:38 -0700 Subject: [PATCH] Avoid crash by handling missing views in dispatchViewManagerCommand --- .../react/uimanager/UIImplementation.java | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 1549f364e2e2f4..eac3475cfcc538 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -21,6 +21,7 @@ import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableArray; import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.events.EventDispatcher; @@ -742,13 +743,21 @@ public void clearJSResponder() { @Deprecated public void dispatchViewManagerCommand( int reactTag, int commandId, @Nullable ReadableArray commandArgs) { - assertViewExists(reactTag, "dispatchViewManagerCommand: " + commandId); + boolean viewExists = checkOrAssertViewExists(reactTag, "dispatchViewManagerCommand: " + commandId); + if (!viewExists) { + return; + } + mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs); } public void dispatchViewManagerCommand( int reactTag, String commandId, @Nullable ReadableArray commandArgs) { - assertViewExists(reactTag, "dispatchViewManagerCommand: " + commandId); + boolean viewExists = checkOrAssertViewExists(reactTag, "dispatchViewManagerCommand: " + commandId); + if (!viewExists) { + return; + } + mOperationsQueue.enqueueDispatchCommand(reactTag, commandId, commandArgs); } @@ -763,7 +772,11 @@ public void dispatchViewManagerCommand( * no arguments if the menu is dismissed */ public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Callback success) { - assertViewExists(reactTag, "showPopupMenu"); + boolean viewExists = checkOrAssertViewExists(reactTag, "showPopupMenu"); + if (!viewExists) { + return; + } + mOperationsQueue.enqueueShowPopupMenu(reactTag, items, error, success); } @@ -867,15 +880,32 @@ private void measureLayoutRelativeToVerifiedAncestor( outputBuffer[3] = node.getScreenHeight(); } - private void assertViewExists(int reactTag, String operationNameForExceptionMessage) { - if (mShadowNodeRegistry.getNode(reactTag) == null) { - throw new IllegalViewOperationException( - "Unable to execute operation " - + operationNameForExceptionMessage - + " on view with " - + "tag: " - + reactTag - + ", since the view does not exists"); + /** + * Returns whether a view identified by the tag exists. In debug mode, this + * will throw whenever the view doesn't exist. In production, it'll log a + * warning. Callers should use this and just return if the view doesn't exist + * to avoid crashing. + */ + private boolean checkOrAssertViewExists(int reactTag, String operationNameForExceptionMessage) { + boolean viewExists = mShadowNodeRegistry.getNode(reactTag) != null; + if (viewExists) { + return true; + } + + String message = "Unable to execute operation " + + operationNameForExceptionMessage + + " on view with " + + "tag: " + + reactTag + + ", since the view does not exists"; + + if (ReactBuildConfig.DEBUG) { + throw new IllegalViewOperationException(message); + } else { + if (!viewExists) { + FLog.w(ReactConstants.TAG, message); + } + return false; } }