Skip to content

Commit

Permalink
feat: add support for TurboModule (#843)
Browse files Browse the repository at this point in the history
Note that this change requires some adjustments to your `build.gradle`:

```diff
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 0c6c69d..2ed70f1 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,6 +1,7 @@
 buildscript {
     def androidTestAppDir = "../node_modules/react-native-test-app/android"
     apply(from: "${androidTestAppDir}/dependencies.gradle")
+    apply(from: "${androidTestAppDir}/test-app-util.gradle")

     repositories {
         mavenCentral()
@@ -8,8 +9,12 @@ buildscript {
     }

     dependencies {
-        classpath "com.android.tools.build:gradle:$androidPluginVersion"
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
+        classpath("com.android.tools.build:gradle:${androidPluginVersion}")
+
+        if (isNewArchitectureEnabled(project)) {
+            classpath("com.facebook.react:react-native-gradle-plugin")
+            classpath("de.undercouch:gradle-download-task:5.1.0")
+        }
     }
 }
```

If you're seeing build issues with `newArchEnabled=true`, please see
https://github.com/microsoft/react-native-test-app/wiki/Troubleshooting.
  • Loading branch information
tido64 committed Jul 22, 2022
1 parent e0b5eae commit 76eddc0
Show file tree
Hide file tree
Showing 38 changed files with 874 additions and 129 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ DEPENDENCIES
xcodeproj!

BUNDLED WITH
2.2.22
2.3.11
128 changes: 114 additions & 14 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ buildscript {

plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
id("org.jetbrains.kotlin.android") version "${kotlinVersion}"
id("org.jetbrains.kotlin.kapt") version "${kotlinVersion}"
}

// `react-native run-android` is hard-coded to look for the output APK at a very
// specific location. See
// https://github.com/react-native-community/cli/blob/6cf12b00c02aca6d4bc843446394331d71a9749e/packages/platform-android/src/commands/runAndroid/index.ts#L180
buildDir = "${rootDir}/${name}/build"

def reactNativeDir = findNodeModulesPath(rootDir, "react-native")
def reactNativeVersion = getReactNativeVersionNumber(rootDir)
def reactNativeDir = findNodeModulesPath("react-native", rootDir)
def reactNativeVersion = getPackageVersionNumber("react-native", rootDir)

repositories {
maven {
Expand Down Expand Up @@ -51,21 +51,23 @@ apply(from: "${projectDir}/../../test-app.gradle")
applyTestAppModule(project)

project.ext.react = [
appName : getAppName(),
applicationId: getApplicationId(),
enableFabric : isFabricEnabled(rootDir),
enableFlipper: getFlipperVersion(rootDir),
enableHermes : true,
abiSplit : false,
appName : getAppName(),
applicationId : getApplicationId(),
architectures : ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"],
enableFabric : isFabricEnabled(project),
enableFlipper : getFlipperVersion(rootDir),
enableHermes : true,
enableNewArchitecture: isNewArchitectureEnabled(project),
]

project.ext.signingConfigs = getSigningConfigs()

android {
compileSdkVersion project.ext.compileSdkVersion

// We need only set `ndkVersion` when building react-native from source.
if (hasProperty("ANDROID_NDK_VERSION")) {
ndkVersion ANDROID_NDK_VERSION
if (project.hasProperty("ndkVersion")) {
ndkVersion project.ext.ndkVersion
}

// TODO: Remove this block when minSdkVersion >= 24. See
Expand Down Expand Up @@ -110,6 +112,69 @@ android {
resValue "string", "app_name", project.ext.react.appName

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

if (project.ext.react.enableNewArchitecture) {
externalNativeBuild {
ndkBuild {
arguments "APP_PLATFORM=android-${project.ext.minSdkVersion}",
"APP_STL=c++_shared",
"NDK_TOOLCHAIN_VERSION=clang",
"GENERATED_SRC_DIR=${buildDir}/generated/source",
"NODE_MODULES_DIR=${reactNativeDir}/..",
"PROJECT_BUILD_DIR=${buildDir}",
"REACT_ANDROID_DIR=${reactNativeDir}/ReactAndroid",
"REACT_ANDROID_BUILD_DIR=${reactNativeDir}/ReactAndroid/build"
cFlags "-Wall", "-Werror", "-frtti", "-fexceptions", "-DWITH_INSPECTOR=1"
cppFlags "-std=c++17"
targets "reacttestapp_appmodules"
}
}
if (!project.ext.react.abiSplit) {
ndk {
abiFilters(*project.ext.react.architectures)
}
}
}
}

if (project.ext.react.enableNewArchitecture) {
externalNativeBuild {
ndkBuild {
path "${projectDir}/src/main/jni/Android.mk"
}
}

def reactAndroidProjectDir = project(":ReactAndroid").projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("${reactAndroidProjectDir}/src/main/jni/prebuilt/lib")
into("${buildDir}/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("${reactAndroidProjectDir}/src/main/jni/prebuilt/lib")
into("${buildDir}/react-ndk/exported")
}

afterEvaluate {
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)

// Due to a bug in AGP, we have to explicitly set a dependency
// between configureNdkBuild* tasks and the preBuild tasks. This can
// be removed once this issue is resolved:
// https://issuetracker.google.com/issues/207403732
configureNdkBuildRelease.dependsOn(preReleaseBuild)
configureNdkBuildDebug.dependsOn(preDebugBuild)
project.ext.react.architectures.each { architecture ->
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}

lintOptions {
Expand Down Expand Up @@ -158,6 +223,13 @@ android {
main.java.srcDirs += "src/no-fabric/java"
}

// TODO: Remove this block when we drop support for 0.65.
if (project.ext.react.enableNewArchitecture) {
main.java.srcDirs += "src/turbomodule/java"
} else {
main.java.srcDirs += "src/no-turbomodule/java"
}

// TODO: Remove this block when we drop support for 0.67.
// https://github.com/facebook/react-native/commit/ce74aa4ed335d4c36ce722d47937b582045e05c4
if (reactNativeVersion < 6800) {
Expand All @@ -166,6 +238,15 @@ android {
main.java.srcDirs += "src/reactinstanceeventlistener-0.68/java"
}
}

splits {
abi {
reset()
enable(project.ext.react.abiSplit)
universalApk(false)
include(*project.ext.react.architectures)
}
}
}

dependencies {
Expand All @@ -175,8 +256,8 @@ dependencies {
// TODO: Remove this block when we drop support for 0.68.
if (reactNativeVersion < 6900) {
def hermesEngineDir =
findNodeModulesPath(file(reactNativeDir), "hermes-engine")
?: findNodeModulesPath(file(reactNativeDir), "hermesvm")
findNodeModulesPath("hermes-engine", file(reactNativeDir))
?: findNodeModulesPath("hermesvm", file(reactNativeDir))
if (hermesEngineDir == null) {
throw new GradleException("Could not find 'hermes-engine'. Please make sure you've added it to 'package.json'.")
}
Expand Down Expand Up @@ -236,3 +317,22 @@ dependencies {
}
}
}

if (project.ext.react.enableNewArchitecture) {
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture, we are building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture, we are building Hermes from source")
}
}
}

// `@react-native-community/cli` currently requires this function to be defined.
// See https://github.com/react-native-community/cli/blob/a87fb9014635fe84ab19a1a88d6ecbbc530eb4e2/packages/platform-android/native_modules.gradle#L497
def isNewArchitectureEnabled() {
return isFabricEnabled(project)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class FabricJSIModulePackage(reactNativeHost: ReactNativeHost) : JSIModulePackag
val componentFactory = ComponentFactory()
CoreComponentsRegistry.register(componentFactory)

ComponentsRegistry.register(componentFactory)

return FabricJSIModuleProvider(
reactApplicationContext,
componentFactory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import android.content.Context
import android.util.Log
import com.facebook.react.PackageList
import com.facebook.react.ReactInstanceManager
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.JSIModulePackage
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReactMarker
Expand All @@ -22,6 +20,7 @@ import com.facebook.soloader.SoLoader
import com.microsoft.reacttestapp.BuildConfig
import com.microsoft.reacttestapp.R
import com.microsoft.reacttestapp.compat.ReactInstanceEventListener
import com.microsoft.reacttestapp.compat.ReactNativeHostCompat
import com.microsoft.reacttestapp.fabric.FabricJSIModulePackage
import java.util.concurrent.CountDownLatch

Expand Down Expand Up @@ -49,7 +48,7 @@ sealed class BundleSource {
class TestAppReactNativeHost(
application: Application,
private val reactBundleNameProvider: ReactBundleNameProvider
) : ReactNativeHost(application) {
) : ReactNativeHostCompat(application) {
var source: BundleSource =
if (reactBundleNameProvider.bundleName == null || isPackagerRunning(application)) {
BundleSource.Server
Expand Down Expand Up @@ -166,7 +165,7 @@ class TestAppReactNativeHost(

override fun getUseDeveloperSupport() = source == BundleSource.Server

override fun getPackages(): List<ReactPackage> = PackageList(application).packages
override fun getPackages() = PackageList(application).packages

private fun addCustomDevOptions(devSupportManager: DevSupportManager) {
val bundleOption = application.resources.getString(
Expand Down
58 changes: 58 additions & 0 deletions android/app/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
THIS_DIR := $(call my-dir)

include $(REACT_ANDROID_DIR)/Android-prebuilt.mk

# If you wish to add a custom TurboModule or Fabric component in your app you
# will have to include the following autogenerated makefile.
# include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk

# Makefile for autolinked libraries
include $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni/Android-rncli.mk

include $(CLEAR_VARS)

LOCAL_PATH := $(THIS_DIR)

# You can customize the name of your application .so file here.
LOCAL_MODULE := reacttestapp_appmodules

LOCAL_C_INCLUDES := $(LOCAL_PATH)
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)

# If you wish to add a custom TurboModule or Fabric component in your app you
# will have to uncomment those lines to include the generated source
# files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
#
# LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
# LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
# LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni

# Autolinked TurboModules and Fabric components
LOCAL_C_INCLUDES += $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni
LOCAL_SRC_FILES += $(wildcard $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni/*.cpp)
LOCAL_EXPORT_C_INCLUDES += $(PROJECT_BUILD_DIR)/generated/rncli/src/main/jni

# Here you should add any native library you wish to depend on.
LOCAL_SHARED_LIBRARIES := \
libfabricjni \
libfbjni \
libfolly_runtime \
libglog \
libjsi \
libreact_codegen_rncore \
libreact_debug \
libreact_nativemodule_core \
libreact_render_componentregistry \
libreact_render_core \
libreact_render_debug \
libreact_render_graphics \
librrc_view \
libruntimeexecutor \
libturbomodulejsijni \
libyoga \
$(call import-codegen-modules)

LOCAL_CFLAGS := -std=c++17 -Wall -frtti -fexceptions -DLOG_TAG=\"ReactNative\"

include $(BUILD_SHARED_LIBRARY)
55 changes: 55 additions & 0 deletions android/app/src/main/jni/ComponentsRegistry.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "ComponentsRegistry.h"

#include <CoreComponentsRegistry.h>
#include <rncli.h>

#include <fbjni/fbjni.h>

#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
#include <react/renderer/components/rncore/ComponentDescriptors.h>

using facebook::react::ComponentDescriptorParameters;
using facebook::react::ComponentDescriptorProviderRegistry;
using facebook::react::ComponentDescriptorRegistry;
using facebook::react::ComponentFactory;
using facebook::react::ContextContainer;
using facebook::react::CoreComponentsRegistry;
using facebook::react::EventDispatcher;
using facebook::react::UnimplementedNativeViewComponentDescriptor;
using ReactTestApp::ComponentsRegistry;

void ComponentsRegistry::registerNatives()
{
registerHybrid({makeNativeMethod("initHybrid", ComponentsRegistry::initHybrid)});
}

ComponentsRegistry::ComponentsRegistry(ComponentFactory *)
{
}

facebook::jni::local_ref<ComponentsRegistry::jhybriddata>
ComponentsRegistry::initHybrid(facebook::jni::alias_ref<jclass>, ComponentFactory *delegate)
{
delegate->buildRegistryFunction = [](EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)
-> ComponentDescriptorRegistry::Shared {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();

// Register providers generated by `@react-native-community/cli`
rncli_registerProviders(providerRegistry);

auto registry = providerRegistry->createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});

auto mutableRegistry = std::const_pointer_cast<ComponentDescriptorRegistry>(registry);

mutableRegistry->setFallbackComponentDescriptor(
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr}));

return registry;
};

return makeCxxInstance(delegate);
}
26 changes: 26 additions & 0 deletions android/app/src/main/jni/ComponentsRegistry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef REACTTESTAPP_JNI_COMPONENTSREGISTRY_H_
#define REACTTESTAPP_JNI_COMPONENTSREGISTRY_H_

#include <ComponentFactory.h>

#include <fbjni/fbjni.h>

namespace ReactTestApp
{
class ComponentsRegistry : public facebook::jni::HybridClass<ComponentsRegistry>
{
public:
constexpr static auto kJavaDescriptor =
"Lcom/microsoft/reacttestapp/fabric/ComponentsRegistry;";

static void registerNatives();

ComponentsRegistry(facebook::react::ComponentFactory *delegate);

private:
static facebook::jni::local_ref<ComponentsRegistry::jhybriddata>
initHybrid(facebook::jni::alias_ref<jclass>, facebook::react::ComponentFactory *delegate);
};
} // namespace ReactTestApp

#endif // REACTTESTAPP_JNI_COMPONENTSREGISTRY_H_
15 changes: 15 additions & 0 deletions android/app/src/main/jni/OnLoad.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <fbjni/fbjni.h>

#include "ComponentsRegistry.h"
#include "TurboModuleManagerDelegate.h"

using ReactTestApp::ComponentsRegistry;
using ReactTestApp::TurboModuleManagerDelegate;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
return facebook::jni::initialize(vm, [] {
TurboModuleManagerDelegate::registerNatives();
ComponentsRegistry::registerNatives();
});
}

0 comments on commit 76eddc0

Please sign in to comment.