diff --git a/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultComponentsRegistry.kt b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultComponentsRegistry.kt new file mode 100644 index 00000000000..23e65c2d4a9 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultComponentsRegistry.kt @@ -0,0 +1,36 @@ +/* + * 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.defaults + +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.facebook.react.fabric.ComponentFactory + +/** + * A utility class that provides users a ComponentRegistry they can customize with a C++ + * implementation of its native methods. + * + * Please note that you need to provide a native implementation for the method initHybrid for this + * class, making sure the Java Descriptor is: + * Lcom/facebook/react/defaults/DefaultComponentsRegistry; + */ +@DoNotStrip +class DefaultComponentsRegistry +@DoNotStrip +private constructor(componentFactory: ComponentFactory) { + + @DoNotStrip private val hybridData: HybridData = initHybrid(componentFactory) + + @DoNotStrip private external fun initHybrid(componentFactory: ComponentFactory): HybridData + + companion object { + @JvmStatic + @DoNotStrip + fun register(componentFactory: ComponentFactory) = DefaultComponentsRegistry(componentFactory) + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultJSIModulePackage.kt b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultJSIModulePackage.kt new file mode 100644 index 00000000000..71b01f5edef --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultJSIModulePackage.kt @@ -0,0 +1,58 @@ +/* + * 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.defaults + +import com.facebook.react.ReactNativeHost +import com.facebook.react.bridge.JSIModulePackage +import com.facebook.react.bridge.JSIModuleProvider +import com.facebook.react.bridge.JSIModuleSpec +import com.facebook.react.bridge.JSIModuleType +import com.facebook.react.bridge.JavaScriptContextHolder +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.UIManager +import com.facebook.react.fabric.ComponentFactory +import com.facebook.react.fabric.FabricJSIModuleProvider +import com.facebook.react.fabric.ReactNativeConfig +import com.facebook.react.uimanager.ViewManagerRegistry + +/** + * A utility class that allows users to create a JSIModulePackage to use Fabric. This essentially + * allows users to just provide C++ implementation for the methods of `DefaultComponentsRegistry` + * without providing all the extra machinery for the New Architecture. + * + * `ReactNativeHost` is required to create Fabric's ViewManagers. + */ +class DefaultJSIModulePackage(private val reactNativeHost: ReactNativeHost) : JSIModulePackage { + + override fun getJSIModules( + reactApplicationContext: ReactApplicationContext, + jsContext: JavaScriptContextHolder + ): List> = + listOf>(JSIModuleForFabric(reactApplicationContext, reactNativeHost)) + + private inner class JSIModuleForFabric( + private val reactApplicationContext: ReactApplicationContext, + private val reactNativeHost: ReactNativeHost + ) : JSIModuleSpec { + override fun getJSIModuleType(): JSIModuleType = JSIModuleType.UIManager + override fun getJSIModuleProvider(): JSIModuleProvider { + val componentFactory = ComponentFactory() + + DefaultComponentsRegistry.register(componentFactory) + + val viewManagers = + reactNativeHost.getReactInstanceManager().getOrCreateViewManagers(reactApplicationContext) + val viewManagerRegistry = ViewManagerRegistry(viewManagers) + return FabricJSIModuleProvider( + reactApplicationContext, + componentFactory, + ReactNativeConfig.DEFAULT_CONFIG, + viewManagerRegistry) + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt new file mode 100644 index 00000000000..841dea31549 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt @@ -0,0 +1,45 @@ +/* + * 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.defaults + +import android.app.Application +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackageTurboModuleManagerDelegate +import com.facebook.react.bridge.JSIModulePackage + +abstract class DefaultReactNativeHost protected constructor(application: Application) : + ReactNativeHost(application) { + + protected override fun getReactPackageTurboModuleManagerDelegateBuilder(): + ReactPackageTurboModuleManagerDelegate.Builder? = + dynamicLibraryName?.let { + // If the user provided a dynamic library name, we assume they want to load + // the default ReactPackageTurboModuleManagerDelegate + DefaultTurboModuleManagerDelegate.Builder(it) + } + + protected override fun getJSIModulePackage(): JSIModulePackage? = + dynamicLibraryName?.let { + // If the user provided a dynamic library name, we assume they want to load + // the default JSIModulePackage + DefaultJSIModulePackage(this) + } + + /** + * Returns the name of the dynamic library used by app on the New Architecture. This is generally + * "_appmodules" + * + * If null, we will assume you're not using the New Architecture and will not attempt to load any + * dynamic library at runtime. + * + * If set, we'll take care of create a TurboModuleManagerDelegate that will load the library you + * specified. + */ + protected open val dynamicLibraryName: String? + get() = null +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultTurboModuleManagerDelegate.kt b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultTurboModuleManagerDelegate.kt new file mode 100644 index 00000000000..314a01ca91c --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultTurboModuleManagerDelegate.kt @@ -0,0 +1,57 @@ +/* + * 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.defaults + +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.facebook.react.ReactPackage +import com.facebook.react.ReactPackageTurboModuleManagerDelegate +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.soloader.SoLoader + +/** + * A utility class that allows you to provide a TurboModuleManagerDelegate by just specifying the + * name of the dynamic library. This class will take care of loading the dynamic library for you on + * your behalf. + * + * Please note that you need to provide a native implementation for the method initHybrid for this + * class, making sure the Java Descriptor is: + * Lcom/facebook/react/defaults/DefaultTurboModuleManagerDelegate; + */ +class DefaultTurboModuleManagerDelegate +private constructor( + dynamicLibraryName: String, + context: ReactApplicationContext, + packages: List +) : ReactPackageTurboModuleManagerDelegate(context, packages) { + + @DoNotStrip protected override external fun initHybrid(): HybridData? + + init { + maybeLoadOtherSoLibraries(dynamicLibraryName) + } + + @Synchronized + private fun maybeLoadOtherSoLibraries(dynamicLibraryName: String) { + // Prevents issues with initializer interruptions. + if (!isSoLibraryLoaded) { + SoLoader.loadLibrary(dynamicLibraryName) + isSoLibraryLoaded = true + } + } + + class Builder(private val dynamicLibraryName: String) : + ReactPackageTurboModuleManagerDelegate.Builder() { + protected override fun build(context: ReactApplicationContext, packages: List) = + DefaultTurboModuleManagerDelegate(dynamicLibraryName, context, packages) + } + + companion object { + @Volatile private var isSoLibraryLoaded = false + } +} diff --git a/packages/rn-tester/android/app/build.gradle b/packages/rn-tester/android/app/build.gradle index 29ed0714a44..9a358595e84 100644 --- a/packages/rn-tester/android/app/build.gradle +++ b/packages/rn-tester/android/app/build.gradle @@ -123,6 +123,13 @@ def reactNativeArchitectures() { return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } +/** + * The name of the dynamic library for this application. This will contain all the + * compiled C++ code and will be loaded at runtime. + * For RN tester is "rntester_appmodules" so that we'll have a `librntester_appmodules.so` to load. + */ +def dynamicLibraryName = "rntester_appmodules" + android { buildToolsVersion = "31.0.0" compileSdkVersion 31 @@ -154,6 +161,7 @@ android { versionName "1.0" testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + buildConfigField("String", "DYNAMIC_LIBRARY_NAME", "\"$dynamicLibraryName\"") } signingConfigs { release { @@ -241,9 +249,8 @@ android { "-DPROJECT_BUILD_DIR=$buildDir", "-DREACT_ANDROID_DIR=$reactAndroidProjectDir", "-DREACT_ANDROID_BUILD_DIR=$reactAndroidBuildDir", + "-DTARGET_NAME=$dynamicLibraryName", "-DANDROID_STL=c++_shared" - - targets "rntester_appmodules" } } } diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java index 6f631b1a165..cf5abe45495 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.java @@ -10,37 +10,24 @@ package com.facebook.react.uiapp; import android.app.Application; import android.content.Context; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.facebook.fbreact.specs.SampleTurboModule; import com.facebook.react.ReactApplication; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; -import com.facebook.react.ReactPackageTurboModuleManagerDelegate; import com.facebook.react.TurboReactPackage; -import com.facebook.react.bridge.JSIModulePackage; -import com.facebook.react.bridge.JSIModuleProvider; -import com.facebook.react.bridge.JSIModuleSpec; -import com.facebook.react.bridge.JSIModuleType; -import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.UIManager; import com.facebook.react.config.ReactFeatureFlags; -import com.facebook.react.fabric.ComponentFactory; -import com.facebook.react.fabric.CoreComponentsRegistry; -import com.facebook.react.fabric.FabricJSIModuleProvider; -import com.facebook.react.fabric.ReactNativeConfig; +import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.react.module.model.ReactModuleInfo; import com.facebook.react.module.model.ReactModuleInfoProvider; import com.facebook.react.shell.MainReactPackage; import com.facebook.react.uiapp.component.MyNativeViewManager; import com.facebook.react.uimanager.ViewManager; -import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.react.views.text.ReactFontManager; import com.facebook.soloader.SoLoader; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -50,7 +37,7 @@ import java.util.Map; public class RNTesterApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = - new ReactNativeHost(this) { + new DefaultReactNativeHost(this) { @Override public String getJSMainModuleName() { return "packages/rn-tester/js/RNTesterApp.android"; @@ -126,54 +113,9 @@ public class RNTesterApplication extends Application implements ReactApplication }); } - @Nullable @Override - protected ReactPackageTurboModuleManagerDelegate.Builder - getReactPackageTurboModuleManagerDelegateBuilder() { - return new RNTesterTurboModuleManagerDelegate.Builder(); - } - - @Nullable - @Override - protected JSIModulePackage getJSIModulePackage() { - return new JSIModulePackage() { - @Override - public List getJSIModules( - final ReactApplicationContext reactApplicationContext, - final JavaScriptContextHolder jsContext) { - final List specs = new ArrayList<>(); - - // Install the new renderer. - specs.add( - new JSIModuleSpec() { - @Override - public JSIModuleType getJSIModuleType() { - return JSIModuleType.UIManager; - } - - @Override - public JSIModuleProvider getJSIModuleProvider() { - final ComponentFactory componentFactory = new ComponentFactory(); - CoreComponentsRegistry.register(componentFactory); - RNTesterComponentsRegistry.register(componentFactory); - final ReactInstanceManager reactInstanceManager = getReactInstanceManager(); - - ViewManagerRegistry viewManagerRegistry = - new ViewManagerRegistry( - reactInstanceManager.getOrCreateViewManagers( - reactApplicationContext)); - - return new FabricJSIModuleProvider( - reactApplicationContext, - componentFactory, - ReactNativeConfig.DEFAULT_CONFIG, - viewManagerRegistry); - } - }); - - return specs; - } - }; + public String getDynamicLibraryName() { + return BuildConfig.DYNAMIC_LIBRARY_NAME; } }; @@ -183,6 +125,7 @@ public class RNTesterApplication extends Application implements ReactApplication ReactFontManager.getInstance().addCustomFont(this, "Rubik", R.font.rubik); super.onCreate(); SoLoader.init(this, /* native exopackage */ false); + SoLoader.loadLibrary(BuildConfig.DYNAMIC_LIBRARY_NAME); initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); } diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterComponentsRegistry.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterComponentsRegistry.java deleted file mode 100644 index 4c0582ba6f5..00000000000 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterComponentsRegistry.java +++ /dev/null @@ -1,35 +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.uiapp; - -import com.facebook.jni.HybridData; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.fabric.ComponentFactory; -import com.facebook.soloader.SoLoader; - -@DoNotStrip -public class RNTesterComponentsRegistry { - static { - SoLoader.loadLibrary("fabricjni"); - } - - @DoNotStrip private final HybridData mHybridData; - - @DoNotStrip - private native HybridData initHybrid(ComponentFactory componentFactory); - - @DoNotStrip - private RNTesterComponentsRegistry(ComponentFactory componentFactory) { - mHybridData = initHybrid(componentFactory); - } - - @DoNotStrip - public static RNTesterComponentsRegistry register(ComponentFactory componentFactory) { - return new RNTesterComponentsRegistry(componentFactory); - } -} diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate.java b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate.java deleted file mode 100644 index bb046c4b37b..00000000000 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate.java +++ /dev/null @@ -1,47 +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.uiapp; - -import androidx.annotation.VisibleForTesting; -import com.facebook.jni.HybridData; -import com.facebook.react.ReactPackage; -import com.facebook.react.ReactPackageTurboModuleManagerDelegate; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.soloader.SoLoader; -import java.util.List; - -/** This class is responsible for creating all the TurboModules for the RNTester app. */ -public class RNTesterTurboModuleManagerDelegate extends ReactPackageTurboModuleManagerDelegate { - private static volatile boolean sIsSoLibraryLoaded; - - protected native HybridData initHybrid(); - - @VisibleForTesting - native boolean canCreateTurboModule(String moduleName); - - private RNTesterTurboModuleManagerDelegate( - ReactApplicationContext context, List packages) { - super(context, packages); - } - - public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder { - protected RNTesterTurboModuleManagerDelegate build( - ReactApplicationContext context, List packages) { - return new RNTesterTurboModuleManagerDelegate(context, packages); - } - } - - @Override - protected synchronized void maybeLoadOtherSoLibraries() { - // Prevents issues with initializer interruptions. - if (!sIsSoLibraryLoaded) { - SoLoader.loadLibrary("rntester_appmodules"); - sIsSoLibraryLoaded = true; - } - } -} diff --git a/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt b/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt index 8458616c6a1..496b839d767 100644 --- a/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt +++ b/packages/rn-tester/android/app/src/main/jni/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.13) # Define the library name here. -project(rntester_appmodules) +project(${TARGET_NAME}) include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterComponentsRegistry.h b/packages/rn-tester/android/app/src/main/jni/RNTesterComponentsRegistry.h index 121dff5cb44..5069583c7c2 100644 --- a/packages/rn-tester/android/app/src/main/jni/RNTesterComponentsRegistry.h +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterComponentsRegistry.h @@ -19,7 +19,7 @@ class RNTesterComponentsRegistry : public facebook::jni::HybridClass { public: constexpr static auto kJavaDescriptor = - "Lcom/facebook/react/uiapp/RNTesterComponentsRegistry;"; + "Lcom/facebook/react/defaults/DefaultComponentsRegistry;"; static void registerNatives(); diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp index 6bf5c8e8afa..c12c1cbdb43 100644 --- a/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.cpp @@ -21,9 +21,6 @@ void RNTesterTurboModuleManagerDelegate::registerNatives() { registerHybrid({ makeNativeMethod( "initHybrid", RNTesterTurboModuleManagerDelegate::initHybrid), - makeNativeMethod( - "canCreateTurboModule", - RNTesterTurboModuleManagerDelegate::canCreateTurboModule), }); } @@ -40,11 +37,5 @@ std::shared_ptr RNTesterTurboModuleManagerDelegate::getTurboModule( return RNTesterAppModuleProvider(name, params); } -bool RNTesterTurboModuleManagerDelegate::canCreateTurboModule( - const std::string &name) { - return getTurboModule(name, nullptr) != nullptr || - getTurboModule(name, {.moduleName = name}) != nullptr; -} - } // namespace react } // namespace facebook diff --git a/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h index d735d4dc521..d7330c252d1 100644 --- a/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h +++ b/packages/rn-tester/android/app/src/main/jni/RNTesterTurboModuleManagerDelegate.h @@ -20,7 +20,7 @@ class RNTesterTurboModuleManagerDelegate TurboModuleManagerDelegate> { public: static constexpr auto kJavaDescriptor = - "Lcom/facebook/react/uiapp/RNTesterTurboModuleManagerDelegate;"; + "Lcom/facebook/react/defaults/DefaultTurboModuleManagerDelegate;"; static jni::local_ref initHybrid(jni::alias_ref); @@ -33,12 +33,6 @@ class RNTesterTurboModuleManagerDelegate const std::string &name, const JavaTurboModule::InitParams ¶ms) override; - /** - * Test-only method. Allows user to verify whether a TurboModule can be - * created by instances of this class. - */ - bool canCreateTurboModule(const std::string &name); - private: friend HybridBase; using HybridBase::HybridBase;