From 05ef779c0b1722ad3827f382f7d3de5d0391afb0 Mon Sep 17 00:00:00 2001 From: Fabrizio Cucci Date: Fri, 12 Apr 2024 01:47:52 -0700 Subject: [PATCH] Push ReactContext logic in derived classes (#44026) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44026 Changelog: [Android][Removed] Delete ReactContext.initializeWithInstance(). ReactContext now no longer contains legacy react instance methods. Please use BridgeReactInstance instead. Yet another attempt to land this (last one was D55505416). Copy-pasting below the amazing summary from RSNara. ## Context Prior, ReactContext used to implement bridge logic. For bridgeless mode, we created BridgelessReactContext < ReactContext ## Problem This could lead to failures: we could call bridge methods in bridgeless mode. ## Changes Primary change: - Make all the react instance methods inside ReactContext abstract. Secondary changes: Implement react instance methods in concrete subclasses: - **New:** BridgeReactContext: By delegating to CatalystInstance - **New:** ThemedReactContext: By delegating to inner ReactContext - **Unchanged:** BridgelessReactContext: By delegating to ReactHost ## Auxiliary changes This fixes ThemedReactContext in bridgeless mode. **Problem:** Prior, ThemedReactContext's react instance methods did not work in bridgeless mode: ThemedReactContext wasn't initialized in bridgeless mode, so all those methods had undefined behaviour. **Solution:** ThemedReactContext now implements all react instance methods, by just forwarding to the initialized ReactContext it decorates (which has an instance). NOTE: Intentionally not converting `BridgeReactContext` to Kotlin to minimize the risk of these changes. Reviewed By: RSNara Differential Revision: D55964787 fbshipit-source-id: b404efe0c7095894fa815165cc8682f78dccfa17 --- .../ReactAndroid/api/ReactAndroid.api | 69 +++++-- .../react/bridge/BridgeReactContext.java | 189 ++++++++++++++++++ .../react/bridge/BridgeReactContext.kt | 20 -- .../facebook/react/bridge/ReactContext.java | 162 ++------------- .../react/runtime/BridgelessReactContext.java | 14 ++ .../react/uimanager/ThemedReactContext.java | 95 +++++++-- 6 files changed, 349 insertions(+), 200 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.java delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index ec8a13a8d75a35..9cf4f232353216 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -543,8 +543,25 @@ public abstract class com/facebook/react/bridge/BaseJavaModule : com/facebook/re public fun invalidate ()V } -public final class com/facebook/react/bridge/BridgeReactContext : com/facebook/react/bridge/ReactApplicationContext { +public class com/facebook/react/bridge/BridgeReactContext : com/facebook/react/bridge/ReactApplicationContext { public fun (Landroid/content/Context;)V + public fun destroy ()V + public fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance; + public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager; + public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule; + public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder; + public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule; + public fun getNativeModules ()Ljava/util/Collection; + public fun getRuntimeExecutor ()Lcom/facebook/react/bridge/RuntimeExecutor; + public fun getSourceURL ()Ljava/lang/String; + public fun handleException (Ljava/lang/Exception;)V + public fun hasActiveCatalystInstance ()Z + public fun hasActiveReactInstance ()Z + public fun hasCatalystInstance ()Z + public fun hasNativeModule (Ljava/lang/Class;)Z + public fun initializeWithInstance (Lcom/facebook/react/bridge/CatalystInstance;)V + public fun isBridgeless ()Z + public fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V } public abstract interface class com/facebook/react/bridge/Callback { @@ -1090,36 +1107,35 @@ public abstract class com/facebook/react/bridge/ReactContext : android/content/C public fun assertOnNativeModulesQueueThread ()V public fun assertOnNativeModulesQueueThread (Ljava/lang/String;)V public fun assertOnUiQueueThread ()V - public fun destroy ()V + public abstract fun destroy ()V public fun emitDeviceEvent (Ljava/lang/String;)V public fun emitDeviceEvent (Ljava/lang/String;Ljava/lang/Object;)V - public fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance; + public abstract fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance; public fun getCurrentActivity ()Landroid/app/Activity; public fun getExceptionHandler ()Lcom/facebook/react/bridge/JSExceptionHandler; - public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager; + public abstract fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager; public fun getJSExceptionHandler ()Lcom/facebook/react/bridge/JSExceptionHandler; public fun getJSMessageQueueThread ()Lcom/facebook/react/bridge/queue/MessageQueueThread; - public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule; - public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder; + public abstract fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule; + public abstract fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder; public fun getLifecycleState ()Lcom/facebook/react/common/LifecycleState; - public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule; - public fun getNativeModules ()Ljava/util/Collection; + public abstract fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule; + public abstract fun getNativeModules ()Ljava/util/Collection; public fun getNativeModulesMessageQueueThread ()Lcom/facebook/react/bridge/queue/MessageQueueThread; - public fun getSourceURL ()Ljava/lang/String; + public abstract fun getSourceURL ()Ljava/lang/String; public fun getSystemService (Ljava/lang/String;)Ljava/lang/Object; public fun getUiMessageQueueThread ()Lcom/facebook/react/bridge/queue/MessageQueueThread; - public fun handleException (Ljava/lang/Exception;)V - public fun hasActiveCatalystInstance ()Z - public fun hasActiveReactInstance ()Z - public fun hasCatalystInstance ()Z + public abstract fun handleException (Ljava/lang/Exception;)V + public abstract fun hasActiveCatalystInstance ()Z + public abstract fun hasActiveReactInstance ()Z + public abstract fun hasCatalystInstance ()Z public fun hasCurrentActivity ()Z - public fun hasNativeModule (Ljava/lang/Class;)Z + public abstract fun hasNativeModule (Ljava/lang/Class;)Z + protected fun initializeFromOther (Lcom/facebook/react/bridge/ReactContext;)V protected fun initializeInteropModules ()V - protected fun initializeInteropModules (Lcom/facebook/react/bridge/ReactContext;)V public fun initializeMessageQueueThreads (Lcom/facebook/react/bridge/queue/ReactQueueConfiguration;)V - public fun initializeWithInstance (Lcom/facebook/react/bridge/CatalystInstance;)V public fun internal_registerInteropModule (Ljava/lang/Class;Ljava/lang/Object;)V - public fun isBridgeless ()Z + public abstract fun isBridgeless ()Z public fun isOnJSQueueThread ()Z public fun isOnNativeModulesQueueThread ()Z public fun isOnUiQueueThread ()Z @@ -1129,7 +1145,7 @@ public abstract class com/facebook/react/bridge/ReactContext : android/content/C public fun onHostResume (Landroid/app/Activity;)V public fun onNewIntent (Landroid/app/Activity;Landroid/content/Intent;)V public fun onWindowFocusChange (Z)V - public fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V + public abstract fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V public fun removeActivityEventListener (Lcom/facebook/react/bridge/ActivityEventListener;)V public fun removeLifecycleEventListener (Lcom/facebook/react/bridge/LifecycleEventListener;)V public fun removeWindowFocusChangeListener (Lcom/facebook/react/bridge/WindowFocusChangeListener;)V @@ -4894,16 +4910,25 @@ public class com/facebook/react/uimanager/ThemedReactContext : com/facebook/reac public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Landroid/content/Context;)V public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Landroid/content/Context;Ljava/lang/String;)V public fun (Lcom/facebook/react/bridge/ReactApplicationContext;Landroid/content/Context;Ljava/lang/String;I)V - public fun addLifecycleEventListener (Lcom/facebook/react/bridge/LifecycleEventListener;)V - public fun getCurrentActivity ()Landroid/app/Activity; + public fun destroy ()V + public fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance; public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager; + public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule; + public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder; public fun getModuleName ()Ljava/lang/String; + public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule; + public fun getNativeModules ()Ljava/util/Collection; public fun getReactApplicationContext ()Lcom/facebook/react/bridge/ReactApplicationContext; + public fun getSourceURL ()Ljava/lang/String; public fun getSurfaceID ()Ljava/lang/String; public fun getSurfaceId ()I - public fun hasCurrentActivity ()Z + public fun handleException (Ljava/lang/Exception;)V + public fun hasActiveCatalystInstance ()Z + public fun hasActiveReactInstance ()Z + public fun hasCatalystInstance ()Z + public fun hasNativeModule (Ljava/lang/Class;)Z public fun isBridgeless ()Z - public fun removeLifecycleEventListener (Lcom/facebook/react/bridge/LifecycleEventListener;)V + public fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V } public class com/facebook/react/uimanager/TouchTargetHelper { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.java new file mode 100644 index 00000000000000..f5fb48b9a90b1e --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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.bridge; + +import android.content.Context; +import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; +import com.facebook.infer.annotation.Assertions; +import com.facebook.infer.annotation.Nullsafe; +import com.facebook.react.bridge.queue.ReactQueueConfiguration; +import com.facebook.react.common.ReactConstants; +import com.facebook.react.common.annotations.DeprecatedInNewArchitecture; +import java.util.Collection; + +/** + * This is the bridge-specific concrete subclass of ReactContext. ReactContext has many methods that + * delegate to the react instance. This subclass implements those methods, by delegating to the + * CatalystInstance. If you need to create a ReactContext within an "bridge context", please create + * BridgeReactContext. + */ +@DeprecatedInNewArchitecture +@Nullsafe(Nullsafe.Mode.LOCAL) +public class BridgeReactContext extends ReactApplicationContext { + + private static final String TAG = "BridgeReactContext"; + + private static final String EARLY_CALL_EXCEPTION_MESSAGE = + "Called %s before setting the Catalyst instance!"; + + private static final String LATE_CALL_EXCEPTION_MESSAGE = + "Called %s after destroying the Catalyst instance!"; + + private volatile boolean mDestroyed = false; + private @Nullable CatalystInstance mCatalystInstance; + + public BridgeReactContext(Context context) { + super(context); + } + + public void initializeWithInstance(CatalystInstance catalystInstance) { + if (catalystInstance == null) { + throw new IllegalArgumentException("CatalystInstance cannot be null."); + } + if (mCatalystInstance != null) { + throw new IllegalStateException("ReactContext has been already initialized"); + } + if (mDestroyed) { + ReactSoftExceptionLogger.logSoftException( + TAG, + new IllegalStateException("Cannot initialize ReactContext after it has been destroyed.")); + } + + mCatalystInstance = catalystInstance; + + ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration(); + initializeMessageQueueThreads(queueConfig); + initializeInteropModules(); + } + + @Override + public void destroy() { + UiThreadUtil.assertOnUiThread(); + + mDestroyed = true; + if (mCatalystInstance != null) { + mCatalystInstance.destroy(); + } + } + + @Override + public CatalystInstance getCatalystInstance() { + return Assertions.assertNotNull(mCatalystInstance); + } + + @Override + public @Nullable UIManager getFabricUIManager() { + CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getFabricUIManager"); + UIManager uiManager = catalystInstance.getFabricUIManager(); + return uiManager != null + ? uiManager + : (UIManager) catalystInstance.getJSIModule(JSIModuleType.UIManager); + } + + @Override + public @Nullable JavaScriptContextHolder getJavaScriptContextHolder() { + return mCatalystInstance != null ? mCatalystInstance.getJavaScriptContextHolder() : null; + } + + @Override + public T getJSModule(Class jsInterface) { + CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getJSModule"); + + if (mInteropModuleRegistry != null + && mInteropModuleRegistry.shouldReturnInteropModule(jsInterface)) { + return mInteropModuleRegistry.getInteropModule(jsInterface); + } + + return catalystInstance.getJSModule(jsInterface); + } + + @Override + public @Nullable T getNativeModule(Class nativeModuleInterface) { + CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getNativeModule"); + return catalystInstance.getNativeModule(nativeModuleInterface); + } + + @Override + public Collection getNativeModules() { + CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getNativeModules"); + return catalystInstance.getNativeModules(); + } + + @Override + public @Nullable RuntimeExecutor getRuntimeExecutor() { + CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getRuntimeExecutor"); + return catalystInstance.getRuntimeExecutor(); + } + + @Override + public @Nullable String getSourceURL() { + return mCatalystInstance != null ? mCatalystInstance.getSourceURL() : null; + } + + @Override + public void handleException(Exception e) { + JSExceptionHandler jsExceptionHandler = getJSExceptionHandler(); + if (hasActiveReactInstance() && jsExceptionHandler != null) { + jsExceptionHandler.handleException(e); + } else { + FLog.e( + ReactConstants.TAG, + "Unable to handle Exception - catalystInstanceVariableExists: " + + (mCatalystInstance != null) + + " - isCatalystInstanceAlive: " + + hasActiveReactInstance() + + " - hasExceptionHandler: " + + (jsExceptionHandler != null), + e); + throw new IllegalStateException(e); + } + } + + @Deprecated + @Override + public boolean hasActiveCatalystInstance() { + return hasActiveReactInstance(); + } + + @Override + public boolean hasActiveReactInstance() { + return mCatalystInstance != null && !mCatalystInstance.isDestroyed(); + } + + @Override + public boolean hasCatalystInstance() { + return mCatalystInstance != null; + } + + @Override + public boolean hasNativeModule(Class nativeModuleInterface) { + CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("hasNativeModule"); + return catalystInstance.hasNativeModule(nativeModuleInterface); + } + + @Override + public boolean isBridgeless() { + return false; + } + + @Override + public void registerSegment(int segmentId, String path, Callback callback) { + Assertions.assertNotNull(mCatalystInstance).registerSegment(segmentId, path); + Assertions.assertNotNull(callback).invoke(); + } + + private CatalystInstance getNonNullableCatalystInstanceOrThrow(String accessTarget) { + if (mCatalystInstance != null) { + return mCatalystInstance; + } + throw new IllegalStateException( + String.format( + mDestroyed ? LATE_CALL_EXCEPTION_MESSAGE : EARLY_CALL_EXCEPTION_MESSAGE, accessTarget)); + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.kt deleted file mode 100644 index 0f37c2fd675f8a..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and 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.bridge - -import android.content.Context -import com.facebook.react.common.annotations.DeprecatedInNewArchitecture - -/** - * This is the bridge-specific concrete subclass of ReactContext. ReactContext has many methods that - * delegate to the react instance. This subclass will implement those methods, by delegating to the - * CatalystInstance. If you need to create a ReactContext within an "bridge context", please create - * BridgeReactContext. - */ -@DeprecatedInNewArchitecture -public class BridgeReactContext(base: Context) : ReactApplicationContext(base) {} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java index 9111da5ebd9fae..03e8726111cfec 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java @@ -25,7 +25,6 @@ import com.facebook.react.bridge.queue.MessageQueueThread; import com.facebook.react.bridge.queue.ReactQueueConfiguration; import com.facebook.react.common.LifecycleState; -import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.DeprecatedInNewArchitecture; import com.facebook.react.common.annotations.FrameworkAPI; import com.facebook.react.common.annotations.UnstableReactNativeAPI; @@ -45,16 +44,6 @@ public interface RCTDeviceEventEmitter extends JavaScriptModule { } private static final String TAG = "ReactContext"; - private static final String EARLY_JS_ACCESS_EXCEPTION_MESSAGE = - "Tried to access a JS module before the React instance was fully set up. Calls to " - + "ReactContext#getJSModule should only happen once initialize() has been called on your " - + "native module."; - private static final String LATE_JS_ACCESS_EXCEPTION_MESSAGE = - "Tried to access a JS module after the React instance was destroyed."; - private static final String EARLY_NATIVE_MODULE_EXCEPTION_MESSAGE = - "Trying to call native module before CatalystInstance has been set!"; - private static final String LATE_NATIVE_MODULE_EXCEPTION_MESSAGE = - "Trying to call native module after CatalystInstance has been destroyed!"; private final CopyOnWriteArraySet mLifecycleEventListeners = new CopyOnWriteArraySet<>(); @@ -65,9 +54,8 @@ public interface RCTDeviceEventEmitter extends JavaScriptModule { private LifecycleState mLifecycleState = LifecycleState.BEFORE_CREATE; - private volatile boolean mDestroyed = false; - private @Nullable CatalystInstance mCatalystInstance; private @Nullable LayoutInflater mInflater; + private @Nullable ReactQueueConfiguration mQueueConfig; private @Nullable MessageQueueThread mUiMessageQueueThread; private @Nullable MessageQueueThread mNativeModulesMessageQueueThread; private @Nullable MessageQueueThread mJSMessageQueueThread; @@ -82,25 +70,9 @@ public ReactContext(Context base) { super(base); } - /** Set and initialize CatalystInstance for this Context. This should be called exactly once. */ - public void initializeWithInstance(CatalystInstance catalystInstance) { - if (catalystInstance == null) { - throw new IllegalArgumentException("CatalystInstance cannot be null."); - } - if (mCatalystInstance != null) { - throw new IllegalStateException("ReactContext has been already initialized"); - } - if (mDestroyed) { - ReactSoftExceptionLogger.logSoftException( - TAG, - new IllegalStateException("Cannot initialize ReactContext after it has been destroyed.")); - } - - mCatalystInstance = catalystInstance; - - ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration(); - initializeMessageQueueThreads(queueConfig); - initializeInteropModules(); + protected void initializeFromOther(ReactContext other) { + initializeMessageQueueThreads(other.mQueueConfig); + mInteropModuleRegistry = other.mInteropModuleRegistry; } /** Initialize message queue threads using a ReactQueueConfiguration. */ @@ -111,6 +83,7 @@ public synchronized void initializeMessageQueueThreads(ReactQueueConfiguration q || mJSMessageQueueThread != null) { throw new IllegalStateException("Message queue threads already initialized"); } + mQueueConfig = queueConfig; mUiMessageQueueThread = queueConfig.getUIQueueThread(); mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread(); mJSMessageQueueThread = queueConfig.getJSQueueThread(); @@ -132,10 +105,6 @@ protected void initializeInteropModules() { mInteropModuleRegistry = new InteropModuleRegistry(); } - protected void initializeInteropModules(ReactContext reactContext) { - mInteropModuleRegistry = reactContext.mInteropModuleRegistry; - } - public void resetPerfStats() { if (mNativeModulesMessageQueueThread != null) { mNativeModulesMessageQueueThread.resetPerfStats(); @@ -149,11 +118,6 @@ public void setJSExceptionHandler(@Nullable JSExceptionHandler jSExceptionHandle mJSExceptionHandler = jSExceptionHandler; } - private void raiseCatalystInstanceMissingException() { - throw new IllegalStateException( - mDestroyed ? LATE_NATIVE_MODULE_EXCEPTION_MESSAGE : EARLY_NATIVE_MODULE_EXCEPTION_MESSAGE); - } - // We override the following method so that views inflated with the inflater obtained from this // context return the ReactContext in #getContext(). The default implementation uses the base // context instead, so it couldn't be cast to ReactContext. @@ -172,44 +136,17 @@ public Object getSystemService(String name) { /** * @return handle to the specified JS module for the CatalystInstance associated with this Context */ - public T getJSModule(Class jsInterface) { - if (mCatalystInstance == null) { - if (mDestroyed) { - throw new IllegalStateException(LATE_JS_ACCESS_EXCEPTION_MESSAGE); - } - throw new IllegalStateException(EARLY_JS_ACCESS_EXCEPTION_MESSAGE); - } - if (mInteropModuleRegistry != null - && mInteropModuleRegistry.shouldReturnInteropModule(jsInterface)) { - return mInteropModuleRegistry.getInteropModule(jsInterface); - } - return mCatalystInstance.getJSModule(jsInterface); - } + public abstract T getJSModule(Class jsInterface); - public boolean hasNativeModule(Class nativeModuleInterface) { - if (mCatalystInstance == null) { - raiseCatalystInstanceMissingException(); - } - return mCatalystInstance.hasNativeModule(nativeModuleInterface); - } + public abstract boolean hasNativeModule(Class nativeModuleInterface); - public Collection getNativeModules() { - if (mCatalystInstance == null) { - raiseCatalystInstanceMissingException(); - } - return mCatalystInstance.getNativeModules(); - } + public abstract Collection getNativeModules(); /** * @return the instance of the specified module interface associated with this ReactContext. */ @Nullable - public T getNativeModule(Class nativeModuleInterface) { - if (mCatalystInstance == null) { - raiseCatalystInstanceMissingException(); - } - return mCatalystInstance.getNativeModule(nativeModuleInterface); - } + public abstract T getNativeModule(Class nativeModuleInterface); /** * @return the RuntimeExecutor, a thread-safe handler for accessing the runtime. @@ -218,12 +155,7 @@ public T getNativeModule(Class nativeModuleInterface @Nullable @FrameworkAPI @UnstableReactNativeAPI - public RuntimeExecutor getRuntimeExecutor() { - if (mCatalystInstance == null) { - raiseCatalystInstanceMissingException(); - } - return mCatalystInstance.getRuntimeExecutor(); - } + public abstract RuntimeExecutor getRuntimeExecutor(); /** * Calls RCTDeviceEventEmitter.emit to JavaScript, with given event name and an optional list of @@ -240,9 +172,7 @@ public void emitDeviceEvent(String eventName) { emitDeviceEvent(eventName, null); } - public CatalystInstance getCatalystInstance() { - return Assertions.assertNotNull(mCatalystInstance); - } + public abstract CatalystInstance getCatalystInstance(); /** * This API has been deprecated due to naming consideration, please use hasActiveReactInstance() @@ -251,20 +181,14 @@ public CatalystInstance getCatalystInstance() { * @return */ @Deprecated - public boolean hasActiveCatalystInstance() { - return hasActiveReactInstance(); - } + public abstract boolean hasActiveCatalystInstance(); /** * @return true if there is an non-null, alive react native instance */ - public boolean hasActiveReactInstance() { - return mCatalystInstance != null && !mCatalystInstance.isDestroyed(); - } + public abstract boolean hasActiveReactInstance(); - public boolean hasCatalystInstance() { - return mCatalystInstance != null; - } + public abstract boolean hasCatalystInstance(); public LifecycleState getLifecycleState() { return mLifecycleState; @@ -380,14 +304,7 @@ public void onHostDestroy() { /** Destroy this instance, making it unusable. */ @ThreadConfined(UI) - public void destroy() { - UiThreadUtil.assertOnUiThread(); - - mDestroyed = true; - if (mCatalystInstance != null) { - mCatalystInstance.destroy(); - } - } + public abstract void destroy(); /** Should be called by the hosting Fragment in {@link Fragment#onActivityResult} */ public void onActivityResult( @@ -480,27 +397,7 @@ public boolean runOnJSQueueThread(Runnable runnable) { * Passes the given exception to the current {@link JSExceptionHandler} if one exists, rethrowing * otherwise. */ - public void handleException(Exception e) { - boolean catalystInstanceVariableExists = mCatalystInstance != null; - boolean isCatalystInstanceAlive = - catalystInstanceVariableExists && !mCatalystInstance.isDestroyed(); - boolean hasExceptionHandler = mJSExceptionHandler != null; - - if (isCatalystInstanceAlive && hasExceptionHandler) { - mJSExceptionHandler.handleException(e); - } else { - FLog.e( - ReactConstants.TAG, - "Unable to handle Exception - catalystInstanceVariableExists: " - + catalystInstanceVariableExists - + " - isCatalystInstanceAlive: " - + isCatalystInstanceAlive - + " - hasExceptionHandler: " - + hasExceptionHandler, - e); - throw new IllegalStateException(e); - } - } + public abstract void handleException(Exception e); public class ExceptionHandlerWrapper implements JSExceptionHandler { @Override @@ -554,9 +451,7 @@ public boolean startActivityForResult(Intent intent, int code, Bundle bundle) { * @deprecated DO NOT USE, this method will be removed in the near future. */ @Deprecated - public boolean isBridgeless() { - return false; - } + public abstract boolean isBridgeless(); /** * Get the C pointer (as a long) to the JavaScriptCore context associated with this instance. Use @@ -564,12 +459,7 @@ public boolean isBridgeless() { * JavaScriptContextHolder jsContext = reactContext.getJavaScriptContextHolder() * synchronized(jsContext) { nativeThingNeedingJsContext(jsContext.get()); } */ - public @Nullable JavaScriptContextHolder getJavaScriptContextHolder() { - if (mCatalystInstance != null) { - return mCatalystInstance.getJavaScriptContextHolder(); - } - return null; - } + public abstract @Nullable JavaScriptContextHolder getJavaScriptContextHolder(); @DeprecatedInNewArchitecture( message = @@ -580,12 +470,7 @@ public boolean isBridgeless() { * * @return The UIManager when CatalystInstance is active. */ - public @Nullable UIManager getFabricUIManager() { - UIManager uiManager = mCatalystInstance.getFabricUIManager(); - return uiManager != null - ? uiManager - : (UIManager) mCatalystInstance.getJSIModule(JSIModuleType.UIManager); - } + public abstract @Nullable UIManager getFabricUIManager(); /** * Get the sourceURL for the JS bundle from the CatalystInstance. This method is needed for @@ -593,18 +478,13 @@ public boolean isBridgeless() { * * @return The JS bundle URL set when the bundle was loaded */ - public @Nullable String getSourceURL() { - return mCatalystInstance == null ? null : mCatalystInstance.getSourceURL(); - } + public abstract @Nullable String getSourceURL(); /** * Register a JS segment after loading it from cache or server, make sure mCatalystInstance is * properly initialised and not null before calling. */ - public void registerSegment(int segmentId, String path, Callback callback) { - Assertions.assertNotNull(mCatalystInstance).registerSegment(segmentId, path); - Assertions.assertNotNull(callback).invoke(); - } + public abstract void registerSegment(int segmentId, String path, Callback callback); /** * Register a {@link JavaScriptModule} within the Interop Layer so that can be consumed whenever diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java index 853cf74be64f5e..74f772b4371779 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessReactContext.java @@ -90,11 +90,25 @@ public CatalystInstance getCatalystInstance() { return new BridgelessCatalystInstance(mReactHost); } + @Deprecated + @Override + public boolean hasActiveCatalystInstance() { + return hasActiveReactInstance(); + } + @Override public boolean hasActiveReactInstance() { return mReactHost.isInstanceInitialized(); } + @Override + public boolean hasCatalystInstance() { + return false; + } + + @Override + public void destroy() {} + DevSupportManager getDevSupportManager() { return mReactHost.getDevSupportManager(); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java index 50c22442af047e..90496c1976f976 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java @@ -7,13 +7,20 @@ package com.facebook.react.uimanager; -import android.app.Activity; import android.content.Context; import androidx.annotation.Nullable; -import com.facebook.react.bridge.LifecycleEventListener; +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.CatalystInstance; +import com.facebook.react.bridge.JavaScriptContextHolder; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.RuntimeExecutor; import com.facebook.react.bridge.UIManager; +import com.facebook.react.common.annotations.FrameworkAPI; +import com.facebook.react.common.annotations.UnstableReactNativeAPI; +import java.util.Collection; /** * Wraps {@link ReactContext} with the base {@link Context} passed into the constructor. It provides @@ -46,33 +53,67 @@ public ThemedReactContext( @Nullable String moduleName, int surfaceId) { super(base); - if (reactApplicationContext.hasCatalystInstance()) { - initializeWithInstance(reactApplicationContext.getCatalystInstance()); + if (reactApplicationContext.hasActiveReactInstance()) { + initializeFromOther(reactApplicationContext); } - initializeInteropModules(reactApplicationContext); mReactApplicationContext = reactApplicationContext; mModuleName = moduleName; mSurfaceId = surfaceId; } @Override - public void addLifecycleEventListener(LifecycleEventListener listener) { - mReactApplicationContext.addLifecycleEventListener(listener); + public T getJSModule(Class jsInterface) { + return mReactApplicationContext.getJSModule(jsInterface); } @Override - public void removeLifecycleEventListener(LifecycleEventListener listener) { - mReactApplicationContext.removeLifecycleEventListener(listener); + public boolean hasNativeModule(Class nativeModuleInterface) { + return mReactApplicationContext.hasNativeModule(nativeModuleInterface); } @Override - public boolean hasCurrentActivity() { - return mReactApplicationContext.hasCurrentActivity(); + public Collection getNativeModules() { + return mReactApplicationContext.getNativeModules(); } + @Nullable @Override - public @Nullable Activity getCurrentActivity() { - return mReactApplicationContext.getCurrentActivity(); + public T getNativeModule(Class nativeModuleInterface) { + return mReactApplicationContext.getNativeModule(nativeModuleInterface); + } + + @Nullable + @Override + @FrameworkAPI + @UnstableReactNativeAPI + public RuntimeExecutor getRuntimeExecutor() { + return mReactApplicationContext.getRuntimeExecutor(); + } + + @Override + public CatalystInstance getCatalystInstance() { + return mReactApplicationContext.getCatalystInstance(); + } + + @Deprecated + @Override + public boolean hasActiveCatalystInstance() { + return mReactApplicationContext.hasActiveCatalystInstance(); + } + + @Override + public boolean hasActiveReactInstance() { + return mReactApplicationContext.hasActiveCatalystInstance(); + } + + @Override + public boolean hasCatalystInstance() { + return mReactApplicationContext.hasCatalystInstance(); + } + + @Override + public void destroy() { + mReactApplicationContext.destroy(); } /** @@ -103,16 +144,36 @@ public ReactApplicationContext getReactApplicationContext() { return mReactApplicationContext; } + @Override + public void handleException(Exception e) { + mReactApplicationContext.handleException(e); + } + + @Deprecated @Override public boolean isBridgeless() { return mReactApplicationContext.isBridgeless(); } + @Nullable + @Override + public JavaScriptContextHolder getJavaScriptContextHolder() { + return mReactApplicationContext.getJavaScriptContextHolder(); + } + @Override public UIManager getFabricUIManager() { - if (isBridgeless()) { - return mReactApplicationContext.getFabricUIManager(); - } - return super.getFabricUIManager(); + return mReactApplicationContext.getFabricUIManager(); + } + + @Nullable + @Override + public String getSourceURL() { + return mReactApplicationContext.getSourceURL(); + } + + @Override + public void registerSegment(int segmentId, String path, Callback callback) { + mReactApplicationContext.registerSegment(segmentId, path, callback); } }