diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java index 8546a5d6e149b7..61d8e0af79f1e2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java @@ -19,7 +19,6 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.Callback; import com.facebook.react.config.ReactFeatureFlags; -import com.facebook.react.interfaces.ReactHost; import com.facebook.react.modules.core.PermissionListener; /** diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactApplication.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactApplication.kt index 3f572fa149eae4..5131e55c4c5a16 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactApplication.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactApplication.kt @@ -8,7 +8,6 @@ package com.facebook.react import com.facebook.react.common.annotations.UnstableReactNativeAPI -import com.facebook.react.interfaces.ReactHost @OptIn(UnstableReactNativeAPI::class) /** Interface that represents an instance of a React Native application */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java index de1ca03272207c..fab2a26dde2842 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java @@ -16,7 +16,6 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.devsupport.DoubleTapReloadRecognizer; -import com.facebook.react.interfaces.ReactHost; import com.facebook.react.interfaces.fabric.ReactSurface; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/ReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt similarity index 96% rename from packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/ReactHost.kt rename to packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt index 4c5ef17ae36481..a1c3ab897cdfb5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/interfaces/ReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -package com.facebook.react.interfaces +package com.facebook.react import android.app.Activity import android.content.Context @@ -13,8 +13,8 @@ import android.os.Bundle import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.queue.ReactQueueConfiguration import com.facebook.react.common.LifecycleState -import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.devsupport.interfaces.DevSupportManager +import com.facebook.react.interfaces.TaskInterface import com.facebook.react.interfaces.fabric.ReactSurface import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler @@ -25,7 +25,6 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler * * The implementation of this interface should be Thread Safe */ -@UnstableReactNativeAPI interface ReactHost { /** The current [LifecycleState] for React Host */ 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 dfb1e641215fbf..b57513cb67216e 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 @@ -71,7 +71,7 @@ public interface RCTDeviceEventEmitter extends JavaScriptModule { private @Nullable JSExceptionHandler mExceptionHandlerWrapper; private @Nullable WeakReference mCurrentActivity; - private @Nullable InteropModuleRegistry mInteropModuleRegistry; + protected @Nullable InteropModuleRegistry mInteropModuleRegistry; private boolean mIsInitialized = false; public ReactContext(Context base) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/UnstableReactNativeAPI.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/UnstableReactNativeAPI.kt index 3996580c313e4d..5ae0b1779a3b24 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/UnstableReactNativeAPI.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/UnstableReactNativeAPI.kt @@ -8,7 +8,7 @@ package com.facebook.react.common.annotations @Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) @RequiresOptIn( level = RequiresOptIn.Level.ERROR, message = "This API is experimental and is likely to change or to be removed in the future") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt index cb8ce917c46680..d481e33c0bd1d9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt @@ -8,20 +8,41 @@ package com.facebook.react.defaults import android.content.Context +import com.facebook.react.JSEngineResolutionAlgorithm +import com.facebook.react.ReactHost +import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage import com.facebook.react.bridge.JSBundleLoader import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.fabric.ComponentFactory -import com.facebook.react.interfaces.ReactHost import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler import com.facebook.react.runtime.JSCInstance import com.facebook.react.runtime.ReactHostImpl import com.facebook.react.runtime.hermes.HermesInstance -@UnstableReactNativeAPI +/** + * A utility class that allows you to simplify the setup of a [ReactHost] for new apps in Open + * Source. + * + * [ReactHost] is an interface responsible of handling the lifecycle of a React Native app when + * running in bridgeless mode. + */ object DefaultReactHost { private var reactHost: ReactHost? = null + /** + * Util function to create a default [ReactHost] to be used in your application. This method is + * used by the New App template. + * + * @param context the Android [Context] to use for creating the [ReactHost] + * @param packageList the list of [ReactPackage]s to use for creating the [ReactHost] + * @param jsMainModulePath the path to your app's main module on Metro. Usually `index` or + * `index.` + * @param jsBundleAssetPath the path to the JS bundle relative to the assets directory. Will be + * composed in a `asset://...` URL + * @param isHermesEnabled whether to use Hermes as the JS engine, default to true. + */ + @OptIn(UnstableReactNativeAPI::class) @JvmStatic fun getDefaultReactHost( context: Context, @@ -47,13 +68,42 @@ object DefaultReactHost { // TODO: T164788699 find alternative of accessing ReactHostImpl for initialising reactHost reactHost = ReactHostImpl( - context, - defaultReactHostDelegate, - componentFactory, - true, - reactJsExceptionHandler, - true) + context, + defaultReactHostDelegate, + componentFactory, + true, + reactJsExceptionHandler, + true) + .apply { + jsEngineResolutionAlgorithm = + if (isHermesEnabled) { + JSEngineResolutionAlgorithm.HERMES + } else { + JSEngineResolutionAlgorithm.JSC + } + } } return reactHost as ReactHost } + + /** + * Util function to create a default [ReactHost] to be used in your application. This method is + * used by the New App template. + * + * This method takes in input a [ReactNativeHost] (bridge-mode) and uses its configuration to + * create an equivalent [ReactHost] (bridgeless-mode). + * + * @param context the Android [Context] to use for creating the [ReactHost] + * @param reactNativeHost the [ReactNativeHost] to use for creating the [ReactHost] + */ + @JvmStatic + fun getDefaultReactHost( + context: Context, + reactNativeHost: ReactNativeHost, + ): ReactHost { + require(reactNativeHost is DefaultReactNativeHost) { + "You can call getDefaultReactHost only with instances of DefaultReactNativeHost" + } + return reactNativeHost.toReactHost(context) + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt index 23c0bada20a220..77cc1a7d1de6b9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt @@ -8,7 +8,9 @@ package com.facebook.react.defaults import android.app.Application +import android.content.Context import com.facebook.react.JSEngineResolutionAlgorithm +import com.facebook.react.ReactHost import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackageTurboModuleManagerDelegate import com.facebook.react.bridge.JSIModulePackage @@ -68,4 +70,18 @@ protected constructor( */ protected open val isHermesEnabled: Boolean? get() = null + + /** + * Converts this [ReactNativeHost] (bridge-mode) to a [ReactHost] (bridgeless-mode). + * + * @param context the Android [Context] to use for creating the [ReactHost] + */ + fun toReactHost(context: Context): ReactHost = + DefaultReactHost.getDefaultReactHost( + context, + packages, + jsMainModuleName, + bundleAssetName ?: "index", + isHermesEnabled ?: true, + ) } 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 de5519395dd043..ad4b76d4b549dc 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 @@ -22,6 +22,7 @@ import com.facebook.react.bridge.ReactNoCrashBridgeNotAllowedSoftException; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.uimanager.events.EventDispatcher; @@ -49,6 +50,9 @@ class BridgelessReactContext extends ReactApplicationContext implements EventDis BridgelessReactContext(Context context, ReactHostImpl host) { super(context); mReactHost = host; + if (ReactFeatureFlags.unstable_useFabricInterop) { + initializeInteropModules(); + } } @Override @@ -124,6 +128,10 @@ public BridgelessJSModuleInvocationHandler( @Override public T getJSModule(Class jsInterface) { + if (mInteropModuleRegistry != null + && mInteropModuleRegistry.shouldReturnInteropModule(jsInterface)) { + return mInteropModuleRegistry.getInteropModule(jsInterface); + } JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance( diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java index a06f19d7ee44e8..b8ed249d140798 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java @@ -25,6 +25,7 @@ import com.facebook.infer.annotation.ThreadSafe; import com.facebook.react.JSEngineResolutionAlgorithm; import com.facebook.react.MemoryPressureRouter; +import com.facebook.react.ReactHost; import com.facebook.react.ReactInstanceEventListener; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.JSBundleLoader; @@ -48,7 +49,6 @@ import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.fabric.ComponentFactory; import com.facebook.react.fabric.FabricUIManager; -import com.facebook.react.interfaces.ReactHost; import com.facebook.react.interfaces.TaskInterface; import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler; import com.facebook.react.interfaces.fabric.ReactSurface; diff --git a/packages/react-native/ReactCommon/react/bridging/Object.h b/packages/react-native/ReactCommon/react/bridging/Object.h index 599b5ceb935788..89e5714b798f0f 100644 --- a/packages/react-native/ReactCommon/react/bridging/Object.h +++ b/packages/react-native/ReactCommon/react/bridging/Object.h @@ -31,7 +31,7 @@ struct Bridging< std::shared_ptr, std::enable_if_t>> { static std::shared_ptr fromJs(jsi::Runtime& rt, const jsi::Object& value) { - return value.asHostObject(rt); + return value.getHostObject(rt); } static jsi::Object toJs(jsi::Runtime& rt, std::shared_ptr value) { diff --git a/packages/react-native/template/android/app/src/main/java/com/helloworld/MainApplication.kt b/packages/react-native/template/android/app/src/main/java/com/helloworld/MainApplication.kt index 7314ad2aad3084..1c38e10c0f7da6 100644 --- a/packages/react-native/template/android/app/src/main/java/com/helloworld/MainApplication.kt +++ b/packages/react-native/template/android/app/src/main/java/com/helloworld/MainApplication.kt @@ -3,17 +3,15 @@ package com.helloworld import android.app.Application import com.facebook.react.PackageList import com.facebook.react.ReactApplication +import com.facebook.react.ReactHost import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage -import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactHost +import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.flipper.ReactNativeFlipper -import com.facebook.react.interfaces.ReactHost import com.facebook.soloader.SoLoader -@UnstableReactNativeAPI class MainApplication : Application(), ReactApplication { override val reactNativeHost: ReactNativeHost = @@ -33,12 +31,7 @@ class MainApplication : Application(), ReactApplication { } override val reactHost: ReactHost - get() = - DefaultReactHost.getDefaultReactHost( - context = this, - packageList = PackageList(this).packages, - jsMainModulePath = "index", - isHermesEnabled = BuildConfig.IS_HERMES_ENABLED) + get() = getDefaultReactHost(this.applicationContext, reactNativeHost) override fun onCreate() { super.onCreate() diff --git a/packages/rn-tester/NativeComponentExample/js/MyNativeView.js b/packages/rn-tester/NativeComponentExample/js/MyNativeView.js index c74c0f084e7648..fe32899209e87a 100644 --- a/packages/rn-tester/NativeComponentExample/js/MyNativeView.js +++ b/packages/rn-tester/NativeComponentExample/js/MyNativeView.js @@ -87,6 +87,7 @@ export default function MyNativeView(props: {}): React.Node { const ref = useRef | null>(null); const legacyRef = useRef | null>(null); const [opacity, setOpacity] = useState(1.0); + const [arrayValues, setArrayValues] = useState([1, 2, 3]); const [hsba, setHsba] = useState(new HSBA()); const [cornerRadiusIndex, setCornerRadiusIndex] = useState(0); const [legacyMeasure, setLegacyMeasure] = @@ -102,7 +103,7 @@ export default function MyNativeView(props: {}): React.Node { ref={ref} style={{flex: 1}} opacity={opacity} - values={[0, 1, 2, 3, 4]} + values={arrayValues} onIntArrayChanged={event => { console.log(event.nativeEvent.values); console.log(event.nativeEvent.boolValues); @@ -154,6 +155,11 @@ export default function MyNativeView(props: {}): React.Node { title="Set Opacity" onPress={() => { setOpacity(Math.random()); + setArrayValues([ + Math.floor(Math.random() * 100), + Math.floor(Math.random() * 100), + Math.floor(Math.random() * 100), + ]); }} />