From 14fb1cc335b900666ef4fe6cec6a23a7a95bcf76 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Sat, 30 Mar 2024 16:53:30 -0700 Subject: [PATCH] Pull react instance method impls out of ReactContext Summary: ## 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). Changelog: [Android][Removed] Delete ReactContext.initializeWithInstance(). ReactContext now no longer contains legacy react instance methods. Please use BridgeReactInstance instead. --- Reviewed By: fkgozali Differential Revision: D55505416 fbshipit-source-id: ce1e3ab379eb788d26130dd44a66544aada3db02 --- .../ReactAndroid/api/ReactAndroid.api | 66 +++++-- .../react/bridge/BridgeReactContext.kt | 185 +++++++++++++++++- .../facebook/react/bridge/ReactContext.java | 162 ++------------- .../react/runtime/BridgelessReactContext.java | 14 ++ .../react/uimanager/ThemedReactContext.java | 95 +++++++-- 5 files changed, 341 insertions(+), 181 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index bde06d29131b3f..888350fcf09eed 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -543,6 +543,22 @@ public abstract class com/facebook/react/bridge/BaseJavaModule : com/facebook/re public final 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 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 final 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 { @@ -1088,36 +1104,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 @@ -1127,7 +1142,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 @@ -4822,16 +4837,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.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/BridgeReactContext.kt index 0f37c2fd675f8a..2adffc7da4a1a0 100644 --- 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 @@ -8,13 +8,194 @@ package com.facebook.react.bridge import android.content.Context +import com.facebook.common.logging.FLog +import com.facebook.react.bridge.queue.ReactQueueConfiguration +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 /** * 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 + * 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 -public class BridgeReactContext(base: Context) : ReactApplicationContext(base) {} +public class BridgeReactContext(base: Context) : ReactApplicationContext(base) { + @Volatile private var destroyed = false + private var catalystInstance: CatalystInstance? = null + + public fun initializeWithInstance(otherCatalystInstance: CatalystInstance?): Unit { + if (otherCatalystInstance == null) { + throw IllegalArgumentException("CatalystInstance cannot be null.") + } + if (catalystInstance != null) { + throw IllegalStateException("ReactContext has been already initialized") + } + if (destroyed) { + ReactSoftExceptionLogger.logSoftException( + TAG, IllegalStateException("Cannot initialize ReactContext after it has been destroyed.")) + } + + catalystInstance = otherCatalystInstance + + val queueConfig: ReactQueueConfiguration = otherCatalystInstance.reactQueueConfiguration + initializeMessageQueueThreads(queueConfig) + initializeInteropModules() + } + + public override fun getJSModule(jsInterface: Class?): T? { + val instance = + catalystInstance + ?: throw IllegalStateException( + if (destroyed) LATE_JS_ACCESS_EXCEPTION_MESSAGE + else EARLY_JS_ACCESS_EXCEPTION_MESSAGE) + + val interopModuleRegistry = mInteropModuleRegistry + if (interopModuleRegistry != null && + interopModuleRegistry.shouldReturnInteropModule(jsInterface)) { + return interopModuleRegistry.getInteropModule(jsInterface) + } + + return instance.getJSModule(jsInterface) + } + + public override fun hasNativeModule( + nativeModuleInterface: Class? + ): Boolean { + val instance = + catalystInstance + ?: throw IllegalStateException( + if (destroyed) LATE_NATIVE_MODULE_EXCEPTION_MESSAGE + else EARLY_NATIVE_MODULE_EXCEPTION_MESSAGE) + return instance.hasNativeModule(nativeModuleInterface) + } + + public override fun getNativeModules(): MutableCollection { + val instance = + catalystInstance + ?: throw IllegalStateException( + if (destroyed) LATE_NATIVE_MODULE_EXCEPTION_MESSAGE + else EARLY_NATIVE_MODULE_EXCEPTION_MESSAGE) + return instance.nativeModules + } + + public override fun getNativeModule(nativeModuleInterface: Class?): T? { + val instance = + catalystInstance + ?: throw IllegalStateException( + if (destroyed) LATE_NATIVE_MODULE_EXCEPTION_MESSAGE + else EARLY_NATIVE_MODULE_EXCEPTION_MESSAGE) + return instance.getNativeModule(nativeModuleInterface) + } + + @FrameworkAPI + @UnstableReactNativeAPI + public override fun getRuntimeExecutor(): RuntimeExecutor? { + val instance = + catalystInstance + ?: throw IllegalStateException( + if (destroyed) LATE_RUNTIME_EXECUTOR_ACCESS_EXCEPTION_MESSAGE + else EARLY_RUNTIME_EXECUTOR_ACCESS_EXCEPTION_MESSAGE) + + return instance.getRuntimeExecutor() + } + + public override fun getCatalystInstance(): CatalystInstance { + return catalystInstance!! + } + + @Deprecated( + "This API is unsupported in the New Architecture.", ReplaceWith("hasActiveReactInstance()")) + public override fun hasActiveCatalystInstance(): Boolean { + return hasActiveReactInstance() + } + + public override fun hasActiveReactInstance(): Boolean { + val instance = catalystInstance + return instance != null && !instance.isDestroyed + } + + public override fun hasCatalystInstance(): Boolean { + return catalystInstance != null + } + + public override fun destroy(): Unit { + UiThreadUtil.assertOnUiThread() + + destroyed = true + catalystInstance?.destroy() + } + + public override fun handleException(e: Exception?): Unit { + val jsExceptionHandler: JSExceptionHandler? = jsExceptionHandler + + if (hasActiveReactInstance() && jsExceptionHandler != null) { + jsExceptionHandler.handleException(e) + } else { + FLog.e( + ReactConstants.TAG, + "Unable to handle Exception - catalystInstanceVariableExists: " + + (catalystInstance != null) + + " - isCatalystInstanceAlive: " + + hasActiveReactInstance() + + " - hasExceptionHandler: " + + (jsExceptionHandler != null), + e) + throw IllegalStateException(e) + } + } + + public override fun isBridgeless(): Boolean { + return false + } + + public override fun getJavaScriptContextHolder(): JavaScriptContextHolder? { + return catalystInstance?.javaScriptContextHolder + } + + public override fun getFabricUIManager(): UIManager? { + val instance = + catalystInstance + ?: throw IllegalStateException( + if (destroyed) LATE_FABRIC_UI_MANAGER_ACCESS_EXCEPTION_MESSAGE + else EARLY_FABRIC_UI_MANAGER_ACCESS_EXCEPTION_MESSAGE) + + return instance.fabricUIManager ?: instance.getJSIModule(JSIModuleType.UIManager) as? UIManager + } + + public override fun getSourceURL(): String? { + return catalystInstance?.sourceURL + } + + public override fun registerSegment(segmentId: Int, path: String?, callback: Callback?): Unit { + catalystInstance!!.registerSegment(segmentId, path) + callback!!.invoke() + } + + private companion object { + private const val TAG = "BridgeReactContext" + + private const val 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 const val LATE_JS_ACCESS_EXCEPTION_MESSAGE = + "Tried to access a JS module after the React instance was destroyed." + private const val EARLY_NATIVE_MODULE_EXCEPTION_MESSAGE = + "Trying to call native module before CatalystInstance has been set!" + private const val LATE_NATIVE_MODULE_EXCEPTION_MESSAGE = + "Trying to call native module after CatalystInstance has been destroyed!" + + private const val EARLY_RUNTIME_EXECUTOR_ACCESS_EXCEPTION_MESSAGE = + "Tried to access a RuntimeExecutor before CatalystInstance has been set!" + private const val LATE_RUNTIME_EXECUTOR_ACCESS_EXCEPTION_MESSAGE = + "Tried to access a RuntimeExecutor after CatalystInstance has been destroyed!" + + private const val LATE_FABRIC_UI_MANAGER_ACCESS_EXCEPTION_MESSAGE = + "Tried to access a FabricUIManager after CatalystInstance has been destroyed!" + private const val EARLY_FABRIC_UI_MANAGER_ACCESS_EXCEPTION_MESSAGE = + "Tried to access a FabricUIManager after CatalystInstance before it has been set!" + } +} 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); } }