diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 31b73c401ba098..0c71c35a2371d8 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -792,6 +792,10 @@ public abstract interface class com/facebook/react/bridge/JSExceptionHandler { public abstract fun handleException (Ljava/lang/Exception;)V } +public abstract interface class com/facebook/react/bridge/JSInstance { + public abstract fun invokeCallback (ILcom/facebook/react/bridge/NativeArrayInterface;)V +} + public class com/facebook/react/bridge/JSONArguments { public fun ()V public static fun fromJSONArray (Lorg/json/JSONArray;)Lcom/facebook/react/bridge/ReadableArray; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java deleted file mode 100644 index e924caf946b125..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ /dev/null @@ -1,147 +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 androidx.annotation.Nullable; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.bridge.queue.ReactQueueConfiguration; -import com.facebook.react.common.annotations.DeprecatedInNewArchitecture; -import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.internal.turbomodule.core.interfaces.TurboModuleRegistry; -import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder; -import com.facebook.react.turbomodule.core.interfaces.NativeMethodCallInvokerHolder; -import java.util.Collection; - -/** - * A higher level API on top of the asynchronous JSC bridge. This provides an environment allowing - * the invocation of JavaScript methods and lets a set of Java APIs be invocable from JavaScript as - * well. - */ -@DoNotStrip -public interface CatalystInstance - extends MemoryPressureListener, JSInstance, JSBundleLoaderDelegate { - void runJSBundle(); - - // Returns the status of running the JS bundle; waits for an answer if runJSBundle is running - boolean hasRunJSBundle(); - - /** - * Return the source URL of the JS Bundle that was run, or {@code null} if no JS bundle has been - * run yet. - */ - @Nullable - String getSourceURL(); - - // This is called from java code, so it won't be stripped anyway, but proguard will rename it, - // which this prevents. - @Override - @DoNotStrip - void invokeCallback(int callbackID, NativeArrayInterface arguments); - - @DoNotStrip - void callFunction(String module, String method, NativeArray arguments); - - /** - * Destroys this catalyst instance, waiting for any other threads in ReactQueueConfiguration - * (besides the UI thread) to finish running. Must be called from the UI thread so that we can - * fully shut down other threads. - */ - void destroy(); - - boolean isDestroyed(); - - /** Initialize all the native modules */ - @VisibleForTesting - void initialize(); - - ReactQueueConfiguration getReactQueueConfiguration(); - - T getJSModule(Class jsInterface); - - boolean hasNativeModule(Class nativeModuleInterface); - - @Nullable - T getNativeModule(Class nativeModuleInterface); - - @Nullable - NativeModule getNativeModule(String moduleName); - - Collection getNativeModules(); - - /** - * This method permits a CatalystInstance to extend the known Native modules. This provided - * registry contains only the new modules to load. - */ - void extendNativeModules(NativeModuleRegistry modules); - - /** - * Adds a idle listener for this Catalyst instance. The listener will receive notifications - * whenever the bridge transitions from idle to busy and vice-versa, where the busy state is - * defined as there being some non-zero number of calls to JS that haven't resolved via a - * onBatchCompleted call. The listener should be purely passive and not affect application logic. - */ - void addBridgeIdleDebugListener(NotThreadSafeBridgeIdleDebugListener listener); - - /** - * Removes a NotThreadSafeBridgeIdleDebugListener previously added with {@link - * #addBridgeIdleDebugListener} - */ - void removeBridgeIdleDebugListener(NotThreadSafeBridgeIdleDebugListener listener); - - /** This method registers the file path of an additional JS segment by its ID. */ - void registerSegment(int segmentId, String path); - - @VisibleForTesting - void setGlobalVariable(String propName, String jsonValue); - - /** - * Do not use this anymore. Use {@link #getRuntimeExecutor()} instead. Get the C pointer (as a - * long) to the JavaScriptCore context associated with this instance. - * - *

Use the following pattern to ensure that the JS context is not cleared while you are using - * it: JavaScriptContextHolder jsContext = reactContext.getJavaScriptContextHolder() - * synchronized(jsContext) { nativeThingNeedingJsContext(jsContext.get()); } - */ - @Deprecated - JavaScriptContextHolder getJavaScriptContextHolder(); - - RuntimeExecutor getRuntimeExecutor(); - - RuntimeScheduler getRuntimeScheduler(); - - /** - * Returns a hybrid object that contains a pointer to a JS CallInvoker, which is used to schedule - * work on the JS Thread. Required for TurboModuleManager initialization. - */ - @Deprecated - CallInvokerHolder getJSCallInvokerHolder(); - - /** - * Returns a hybrid object that contains a pointer to a NativeMethodCallInvoker, which is used to - * schedule work on the NativeModules thread. Required for TurboModuleManager initialization. - */ - NativeMethodCallInvokerHolder getNativeMethodCallInvokerHolder(); - - @DeprecatedInNewArchitecture( - message = - "This method will be deprecated later as part of Stable APIs with bridge removal and not" - + " encouraged usage.") - void setTurboModuleRegistry(TurboModuleRegistry turboModuleRegistry); - - @DeprecatedInNewArchitecture( - message = - "This method will be deprecated later as part of Stable APIs with bridge removal and not" - + " encouraged usage.") - void setFabricUIManager(UIManager fabricUIManager); - - @DeprecatedInNewArchitecture( - message = - "This method will be deprecated later as part of Stable APIs with bridge removal and not" - + " encouraged usage.") - UIManager getFabricUIManager(); -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.kt new file mode 100644 index 00000000000000..31e24cf7276342 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.kt @@ -0,0 +1,120 @@ +/* + * 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 com.facebook.proguard.annotations.DoNotStrip +import com.facebook.react.bridge.queue.ReactQueueConfiguration +import com.facebook.react.common.annotations.DeprecatedInNewArchitecture +import com.facebook.react.common.annotations.VisibleForTesting +import com.facebook.react.internal.turbomodule.core.interfaces.TurboModuleRegistry +import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder +import com.facebook.react.turbomodule.core.interfaces.NativeMethodCallInvokerHolder + +/** + * A higher level API on top of the asynchronous JSC bridge. This provides an environment allowing + * the invocation of JavaScript methods and lets a set of Java APIs be invocable from JavaScript as + * well. + */ +@DoNotStrip +public interface CatalystInstance : MemoryPressureListener, JSInstance, JSBundleLoaderDelegate { + public fun runJSBundle() + + // Returns the status of running the JS bundle; waits for an answer if runJSBundle is running + public fun hasRunJSBundle(): Boolean + + /** + * Return the source URL of the JS Bundle that was run, or `null` if no JS bundle has been run + * yet. + */ + public val sourceURL: String? + + // This is called from java code, so it won't be stripped anyway, but proguard will rename it, + // which this prevents. + @DoNotStrip public override fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface) + + @DoNotStrip public fun callFunction(module: String, method: String, arguments: NativeArray?) + + /** + * Destroys this catalyst instance, waiting for any other threads in ReactQueueConfiguration + * (besides the UI thread) to finish running. Must be called from the UI thread so that we can + * fully shut down other threads. + */ + public fun destroy() + + public val isDestroyed: Boolean + + /** Initialize all the native modules */ + @VisibleForTesting public fun initialize() + + public val reactQueueConfiguration: ReactQueueConfiguration? + + public fun getJSModule(jsInterface: Class): T? + + public fun hasNativeModule(nativeModuleInterface: Class): Boolean + + public fun getNativeModule(nativeModuleInterface: Class): T? + + public fun getNativeModule(moduleName: String): NativeModule? + + public val nativeModules: Collection + + /** + * This method permits a CatalystInstance to extend the known Native modules. This provided + * registry contains only the new modules to load. + */ + public fun extendNativeModules(modules: NativeModuleRegistry) + + /** + * Adds a idle listener for this Catalyst instance. The listener will receive notifications + * whenever the bridge transitions from idle to busy and vice-versa, where the busy state is + * defined as there being some non-zero number of calls to JS that haven't resolved via a + * onBatchCompleted call. The listener should be purely passive and not affect application logic. + */ + public fun addBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) + + /** + * Removes a NotThreadSafeBridgeIdleDebugListener previously added with + * [ ][.addBridgeIdleDebugListener] + */ + public fun removeBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) + + /** This method registers the file path of an additional JS segment by its ID. */ + public fun registerSegment(segmentId: Int, path: String) + + @VisibleForTesting public fun setGlobalVariable(propName: String, jsonValue: String) + + @get:Deprecated("") public val javaScriptContextHolder: JavaScriptContextHolder? + public val runtimeExecutor: RuntimeExecutor? + public val runtimeScheduler: RuntimeScheduler? + + @get:Deprecated("") public val jSCallInvokerHolder: CallInvokerHolder? + + /** + * Returns a hybrid object that contains a pointer to a NativeMethodCallInvoker, which is used to + * schedule work on the NativeModules thread. Required for TurboModuleManager initialization. + */ + public val nativeMethodCallInvokerHolder: NativeMethodCallInvokerHolder? + + @DeprecatedInNewArchitecture( + message = + "This method will be deprecated later as part of Stable APIs with bridge removal and not" + + " encouraged usage.") + public fun setTurboModuleRegistry(turboModuleRegistry: TurboModuleRegistry) + + @DeprecatedInNewArchitecture( + message = + "This method will be deprecated later as part of Stable APIs with bridge removal and not" + + " encouraged usage.") + public fun setFabricUIManager(fabricUIManager: UIManager) + + @DeprecatedInNewArchitecture( + message = + "This method will be deprecated later as part of Stable APIs with bridge removal and not" + + " encouraged usage.") + public fun getFabricUIManager(): UIManager? +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.kt index b5d5c2d3b1a24f..193adcd9c0e5cd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JSInstance.kt @@ -11,7 +11,7 @@ package com.facebook.react.bridge * This interface includes the methods needed to use a running JS instance, without specifying any * of the bridge-specific initialization or lifecycle management. */ -internal interface JSInstance { +public interface JSInstance { public fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface) // TODO if this interface survives refactoring, think about adding diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt index 10bc63a9c98e2b..6ca3d0d4f4f943 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessCatalystInstance.kt @@ -63,16 +63,12 @@ public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : throw UnsupportedOperationException("Unimplemented method 'hasRunJSBundle'") } - override fun getSourceURL(): String? { - throw UnsupportedOperationException("Unimplemented method 'getSourceURL'") - } - @DoNotStrip override fun invokeCallback(callbackID: Int, arguments: NativeArrayInterface) { throw UnsupportedOperationException("Unimplemented method 'invokeCallback'") } - override fun callFunction(module: String, method: String, arguments: NativeArray) { + override fun callFunction(module: String, method: String, arguments: NativeArray?) { throw UnsupportedOperationException("Unimplemented method 'callFunction'") } @@ -80,20 +76,27 @@ public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : throw UnsupportedOperationException("Unimplemented method 'destroy'") } - override fun isDestroyed(): Boolean { - throw UnsupportedOperationException("Unimplemented method 'isDestroyed'") - } + override public val isDestroyed: Boolean + get() = throw UnsupportedOperationException("Unimplemented method 'isDestroyed'") @VisibleForTesting override fun initialize() { throw UnsupportedOperationException("Unimplemented method 'initialize'") } - override fun getReactQueueConfiguration(): ReactQueueConfiguration = - reactHost.reactQueueConfiguration!! + override fun getJSModule(jsInterface: Class): T? = + reactHost.currentReactContext?.getJSModule(jsInterface) + + override public val javaScriptContextHolder: JavaScriptContextHolder? + get() = reactHost.getJavaScriptContextHolder() + + override public val jSCallInvokerHolder: CallInvokerHolder? + get() = reactHost.getJSCallInvokerHolder() - override fun getJSModule(jsInterface: Class): T = - reactHost.currentReactContext?.getJSModule(jsInterface)!! + override public val nativeMethodCallInvokerHolder: NativeMethodCallInvokerHolder? + get() = + throw UnsupportedOperationException( + "Unimplemented method 'getNativeMethodCallInvokerHolder'") override fun hasNativeModule(nativeModuleInterface: Class): Boolean = reactHost.hasNativeModule(nativeModuleInterface) @@ -104,12 +107,25 @@ public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : override fun getNativeModule(moduleName: String): NativeModule? = reactHost.getNativeModule(moduleName) - override fun getNativeModules(): Collection = reactHost.getNativeModules() + override public val nativeModules: Collection + get() = reactHost.getNativeModules() - override fun extendNativeModules(modules: NativeModuleRegistry) { + override public val reactQueueConfiguration: ReactQueueConfiguration? + get() = reactHost.reactQueueConfiguration + + override public val runtimeExecutor: RuntimeExecutor? + get() = reactHost.getRuntimeExecutor() + + override public val runtimeScheduler: RuntimeScheduler? + get() = throw UnsupportedOperationException("Unimplemented method 'getRuntimeScheduler'") + + override public fun extendNativeModules(modules: NativeModuleRegistry) { throw UnsupportedOperationException("Unimplemented method 'extendNativeModules'") } + override public val sourceURL: String? + get() = throw UnsupportedOperationException("Unimplemented method 'getSourceURL'") + override fun addBridgeIdleDebugListener(listener: NotThreadSafeBridgeIdleDebugListener) { throw UnsupportedOperationException("Unimplemented method 'addBridgeIdleDebugListener'") } @@ -127,27 +143,6 @@ public class BridgelessCatalystInstance(private val reactHost: ReactHostImpl) : throw UnsupportedOperationException("Unimplemented method 'setGlobalVariable'") } - @Deprecated(message = "This API is unsupported in the New Architecture.") - override fun getJavaScriptContextHolder(): JavaScriptContextHolder? { - return reactHost.getJavaScriptContextHolder() - } - - override fun getRuntimeExecutor(): RuntimeExecutor? { - return reactHost.getRuntimeExecutor() - } - - override fun getRuntimeScheduler(): RuntimeScheduler { - throw UnsupportedOperationException("Unimplemented method 'getRuntimeScheduler'") - } - - override fun getJSCallInvokerHolder(): CallInvokerHolder? { - return reactHost.getJSCallInvokerHolder() - } - - override fun getNativeMethodCallInvokerHolder(): NativeMethodCallInvokerHolder { - throw UnsupportedOperationException("Unimplemented method 'getNativeMethodCallInvokerHolder'") - } - @DeprecatedInNewArchitecture( message = "This method will be deprecated later as part of Stable APIs with bridge removal and not encouraged usage.") diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.kt index 69d96486b6d59f..8de19b6fd58818 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/ReactTestHelper.kt @@ -39,10 +39,10 @@ object ReactTestHelper { val reactQueueConfiguration: ReactQueueConfiguration = ReactQueueConfigurationImpl.create(spec) { e -> throw RuntimeException(e) } val reactInstance: CatalystInstance = mock(CatalystInstance::class.java) - whenever(reactInstance.getReactQueueConfiguration()).thenReturn(reactQueueConfiguration) + whenever(reactInstance.reactQueueConfiguration).thenReturn(reactQueueConfiguration) whenever(reactInstance.getNativeModule(UIManagerModule::class.java)) .thenReturn(mock(UIManagerModule::class.java)) - whenever(reactInstance.isDestroyed()).thenReturn(false) + whenever(reactInstance.isDestroyed).thenReturn(false) return reactInstance } }