Skip to content

Commit

Permalink
Provide defaults for TurboModuleManagerDelegate and JSIModulePackage (#…
Browse files Browse the repository at this point in the history
…34418)

Summary:
Pull Request resolved: #34418

This is an attempt to relax the need of specifying a custom `TurboModuleManagerDelegate` and a `JSIModulePackage` in new apps for New Architecture.
Users can just specify the name of the dynamic library to load and they'll default to use the `DefaultTurboModuleManagerDelegate` and `DefaultJSIModulePackage`.

Users will still have to provide a C++ implementation for it for the time being, but this at least removes
one extra file that we requested them to create and maintain.

If we're fine with this approach, I'll replicate it inside the default template.

Changelog:

[Android] [Added] - Provide defaults for TurboModuleManagerDelegate and JSIModulePackage

Reviewed By: cipolleschi

Differential Revision: D38701180

fbshipit-source-id: eec302c5789990700eb75353d97751358ca6799f
  • Loading branch information
cortinico authored and facebook-github-bot committed Aug 16, 2022
1 parent d35aab4 commit 9a2eb90
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 164 deletions.
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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<JSIModuleSpec<UIManager>> =
listOf<JSIModuleSpec<UIManager>>(JSIModuleForFabric(reactApplicationContext, reactNativeHost))

private inner class JSIModuleForFabric(
private val reactApplicationContext: ReactApplicationContext,
private val reactNativeHost: ReactNativeHost
) : JSIModuleSpec<UIManager> {
override fun getJSIModuleType(): JSIModuleType = JSIModuleType.UIManager
override fun getJSIModuleProvider(): JSIModuleProvider<UIManager> {
val componentFactory = ComponentFactory()

DefaultComponentsRegistry.register(componentFactory)

val viewManagers =
reactNativeHost.getReactInstanceManager().getOrCreateViewManagers(reactApplicationContext)
val viewManagerRegistry = ViewManagerRegistry(viewManagers)
return FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
ReactNativeConfig.DEFAULT_CONFIG,
viewManagerRegistry)
}
}
}
Original file line number Diff line number Diff line change
@@ -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
* "<applicationname>_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
}
Original file line number Diff line number Diff line change
@@ -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<ReactPackage>
) : 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<ReactPackage>) =
DefaultTurboModuleManagerDelegate(dynamicLibraryName, context, packages)
}

companion object {
@Volatile private var isSoLibraryLoaded = false
}
}
11 changes: 9 additions & 2 deletions packages/rn-tester/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,24 @@
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;
Expand All @@ -50,7 +37,7 @@
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";
Expand Down Expand Up @@ -126,54 +113,9 @@ public List<ViewManager> createViewManagers(
});
}

@Nullable
@Override
protected ReactPackageTurboModuleManagerDelegate.Builder
getReactPackageTurboModuleManagerDelegateBuilder() {
return new RNTesterTurboModuleManagerDelegate.Builder();
}

@Nullable
@Override
protected JSIModulePackage getJSIModulePackage() {
return new JSIModulePackage() {
@Override
public List<JSIModuleSpec> getJSIModules(
final ReactApplicationContext reactApplicationContext,
final JavaScriptContextHolder jsContext) {
final List<JSIModuleSpec> specs = new ArrayList<>();

// Install the new renderer.
specs.add(
new JSIModuleSpec() {
@Override
public JSIModuleType getJSIModuleType() {
return JSIModuleType.UIManager;
}

@Override
public JSIModuleProvider<UIManager> 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;
}
};

Expand All @@ -183,6 +125,7 @@ public void onCreate() {
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());
}

Expand Down

This file was deleted.

0 comments on commit 9a2eb90

Please sign in to comment.