Provide defaults for TurboModuleManagerDelegate and JSIModulePackage (#34418)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/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
This commit is contained in:
Nicola Corti
2022-08-16 07:55:40 -07:00
committed by Facebook GitHub Bot
parent d35aab45b9
commit 9a2eb9089f
12 changed files with 213 additions and 164 deletions
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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
}
@@ -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
}
}