diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java index 4eff054efe9c3b..039798dcaf321f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java @@ -10,8 +10,6 @@ import android.app.Activity; import android.content.Context; import androidx.annotation.Nullable; -import com.facebook.react.bridge.JSIModule; -import com.facebook.react.bridge.JSIModuleType; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; @@ -71,16 +69,12 @@ public boolean hasCurrentActivity() { return mSurfaceID; } - @Override - public boolean isBridgeless() { - return mReactApplicationContext.isBridgeless(); + public ReactApplicationContext getReactApplicationContext() { + return mReactApplicationContext; } @Override - public JSIModule getJSIModule(JSIModuleType moduleType) { - if (isBridgeless()) { - return mReactApplicationContext.getJSIModule(moduleType); - } - return super.getJSIModule(moduleType); + public boolean isBridgeless() { + return mReactApplicationContext.isBridgeless(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java index c034e06830b069..bd978336c2d0c8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java @@ -22,6 +22,7 @@ import com.facebook.react.bridge.UIManager; import com.facebook.react.uimanager.common.UIManagerType; import com.facebook.react.uimanager.events.EventDispatcher; +import com.facebook.react.uimanager.events.EventDispatcherProvider; /** Helper class for {@link UIManager}. */ public class UIManagerHelper { @@ -43,32 +44,28 @@ private static UIManager getUIManager( ReactContext context, @UIManagerType int uiManagerType, boolean returnNullIfCatalystIsInactive) { - if (context.isBridgeless()) { - return (UIManager) context.getJSIModule(JSIModuleType.UIManager); - } else { - if (!context.hasCatalystInstance()) { - ReactSoftException.logSoftException( - "UIManagerHelper", - new ReactNoCrashSoftException( - "Cannot get UIManager because the context doesn't contain a CatalystInstance.")); + if (!context.hasCatalystInstance()) { + ReactSoftException.logSoftException( + "UIManagerHelper", + new ReactNoCrashSoftException( + "Cannot get UIManager because the context doesn't contain a CatalystInstance.")); + return null; + } + // TODO T60461551: add tests to verify emission of events when the ReactContext is being turn + // down. + if (!context.hasActiveCatalystInstance()) { + ReactSoftException.logSoftException( + "UIManagerHelper", + new ReactNoCrashSoftException( + "Cannot get UIManager because the context doesn't contain an active CatalystInstance.")); + if (returnNullIfCatalystIsInactive) { return null; } - // TODO T60461551: add tests to verify emission of events when the ReactContext is being turn - // down. - if (!context.hasActiveCatalystInstance()) { - ReactSoftException.logSoftException( - "UIManagerHelper", - new ReactNoCrashSoftException( - "Cannot get UIManager because the context doesn't contain an active CatalystInstance.")); - if (returnNullIfCatalystIsInactive) { - return null; - } - } - CatalystInstance catalystInstance = context.getCatalystInstance(); - return uiManagerType == FABRIC - ? (UIManager) catalystInstance.getJSIModule(JSIModuleType.UIManager) - : catalystInstance.getNativeModule(UIManagerModule.class); } + CatalystInstance catalystInstance = context.getCatalystInstance(); + return uiManagerType == FABRIC + ? (UIManager) catalystInstance.getJSIModule(JSIModuleType.UIManager) + : catalystInstance.getNativeModule(UIManagerModule.class); } /** @@ -87,6 +84,13 @@ public static EventDispatcher getEventDispatcherForReactTag(ReactContext context @Nullable public static EventDispatcher getEventDispatcher( ReactContext context, @UIManagerType int uiManagerType) { + // TODO T67518514 Clean this up once we migrate everything over to bridgeless mode + if (context.isBridgeless()) { + if (context instanceof ThemedReactContext) { + context = ((ThemedReactContext) context).getReactApplicationContext(); + } + return ((EventDispatcherProvider) context).getEventDispatcher(); + } UIManager uiManager = getUIManager(context, uiManagerType, false); return uiManager == null ? null : (EventDispatcher) uiManager.getEventDispatcher(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java new file mode 100644 index 00000000000000..1fce7974087b6a --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/BlackHoleEventDispatcher.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager.events; + +import com.facebook.common.logging.FLog; + +/** + * A singleton class that overrides {@link EventDispatcher} with no-op methods, to be used by + * callers that expect an EventDispatcher when the instance doesn't exist. + */ +public class BlackHoleEventDispatcher implements EventDispatcher { + + private static final EventDispatcher sEventDispatcher = new BlackHoleEventDispatcher(); + + public static EventDispatcher get() { + return sEventDispatcher; + } + + private BlackHoleEventDispatcher() {} + + @Override + public void dispatchEvent(Event event) { + FLog.d( + getClass().getSimpleName(), + "Trying to emit event to JS, but the React instance isn't ready. Event: " + + event.getEventName()); + } + + @Override + public void dispatchAllEvents() {} + + @Override + public void addListener(EventDispatcherListener listener) {} + + @Override + public void removeListener(EventDispatcherListener listener) {} + + @Override + public void addBatchEventDispatchedListener(BatchEventDispatchedListener listener) {} + + @Override + public void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener) {} + + @Override + public void registerEventEmitter(int uiManagerType, RCTEventEmitter eventEmitter) {} + + @Override + public void unregisterEventEmitter(int uiManagerType) {} + + @Override + public void onCatalystInstanceDestroyed() {} +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.java new file mode 100644 index 00000000000000..6f6537c068cf50 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/EventDispatcherProvider.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.uimanager.events; + +/** + * An interface that can be implemented by a {@link com.facebook.react.bridge.ReactContext} to + * provide a first-class API for accessing the {@link EventDispatcher} from the {@link + * com.facebook.react.bridge.UIManager}. + */ +public interface EventDispatcherProvider { + + /** + * This method should always return an EventDispatcher, even if the instance doesn't exist; in + * that case it should return the empty {@link BlackHoleEventDispatcher}. + * + * @return An {@link EventDispatcher} to emit events to JS. + */ + EventDispatcher getEventDispatcher(); +}