From f7f5b4913ae96cd4977c50d82a0cc5583e85e6d8 Mon Sep 17 00:00:00 2001 From: Dmitry Rykun Date: Wed, 19 Jul 2023 10:09:03 -0700 Subject: [PATCH] Android: Introduce RN$LegacyInterop_UIManager_getConstants (#38153) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/38153 This diff adds Android specific implementation of `RN$LegacyInterop_UIManager_getConstants` and binds it to JS runtime. It is supposed to be used as a substitute to UIManager.getConstants in bridgeless mode. Changelog: [Internal] - Introduce RN$LegacyInterop_UIManager_getConstants in Android. Reviewed By: RSNara Differential Revision: D45773342 fbshipit-source-id: 194aa5e940743b4d2c242798764a4207e8b1334f --- .../react/bridgeless/ReactInstance.java | 41 ++++++++++++ .../react/config/ReactFeatureFlags.java | 3 + .../react/uimanager/UIConstantsProvider.java | 18 ++++++ .../uimanager/UIConstantsProviderManager.java | 41 ++++++++++++ .../react/uimanager/UIManagerModule.java | 2 +- .../ReactAndroid/src/main/jni/CMakeLists.txt | 1 + .../main/jni/react/uimanager/CMakeLists.txt | 1 + .../src/main/jni/react/uimanager/OnLoad.cpp | 2 + .../uimanager/UIConstantsProviderManager.cpp | 64 +++++++++++++++++++ .../uimanager/UIConstantsProviderManager.h | 47 ++++++++++++++ .../nativeviewconfig/CMakeLists.txt | 19 ++++++ 11 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProvider.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProviderManager.java create mode 100644 packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.cpp create mode 100644 packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.h create mode 100644 packages/react-native/ReactCommon/react/bridgeless/nativeviewconfig/CMakeLists.txt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridgeless/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridgeless/ReactInstance.java index e756dd6740258f..4f111194952023 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridgeless/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridgeless/ReactInstance.java @@ -19,10 +19,12 @@ import com.facebook.react.BridgelessReactPackage; import com.facebook.react.ReactPackage; import com.facebook.react.ViewManagerOnDemandReactPackage; +import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.JSBundleLoader; import com.facebook.react.bridge.JSBundleLoaderDelegate; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.NativeArray; +import com.facebook.react.bridge.NativeMap; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.RuntimeExecutor; @@ -33,6 +35,7 @@ import com.facebook.react.bridge.queue.ReactQueueConfiguration; import com.facebook.react.bridge.queue.ReactQueueConfigurationImpl; import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; +import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.fabric.Binding; import com.facebook.react.fabric.BindingImpl; @@ -52,6 +55,9 @@ import com.facebook.react.uimanager.ComponentNameResolverManager; import com.facebook.react.uimanager.DisplayMetricsHolder; import com.facebook.react.uimanager.IllegalViewOperationException; +import com.facebook.react.uimanager.UIConstantsProvider; +import com.facebook.react.uimanager.UIConstantsProviderManager; +import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.react.uimanager.ViewManagerResolver; @@ -60,8 +66,10 @@ import com.facebook.systrace.Systrace; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.Nullable; @@ -87,6 +95,7 @@ final class ReactInstance { private final JavaTimerManager mJavaTimerManager; @DoNotStrip @Nullable private ComponentNameResolverManager mComponentNameResolverManager; + @DoNotStrip @Nullable private UIConstantsProviderManager mUIConstantsProviderManager; static { loadLibraryIfNeeded(); @@ -220,6 +229,27 @@ public void onHostDestroy() { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); + // Initialize function for JS's UIManager.getViewManagerConfig() + // It should come after getTurboModuleManagerDelegate as it relies on react packages being + // initialized. + // This happens inside getTurboModuleManagerDelegate getter. + if (ReactFeatureFlags.useNativeViewConfigsInBridgelessMode) { + mUIConstantsProviderManager = + new UIConstantsProviderManager( + // Use unbuffered RuntimeExecutor to install binding + unbufferedRuntimeExecutor, + // Here we are construncting the return value for UIManager.getConstants call. + // The old architectre relied on the constatnts struct to contain: + // 1. Eagerly loaded view configs for all native components. + // 2. genericBubblingEventTypes. + // 3. genericDirectEventTypes. + // We want to match this beahavior. + (UIConstantsProvider) + () -> { + return getUIManagerConstants(); + }); + } + // Set up Fabric Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstance.initialize#initFabric"); @@ -388,6 +418,7 @@ public Collection getNativeModules() { mFabricUIManager.onCatalystInstanceDestroy(); mHybridData.resetNative(); mComponentNameResolverManager = null; + mUIConstantsProviderManager = null; } /* --- Native methods --- */ @@ -500,4 +531,14 @@ public void registerSegment(int segmentId, String path) { } return uniqueNames; } + + private @NonNull NativeMap getUIManagerConstants() { + List viewManagers = new ArrayList(); + for (String viewManagerName : getViewManagerNames()) { + viewManagers.add(createViewManager(viewManagerName)); + } + Map constants = + UIManagerModule.createConstants(viewManagers, new HashMap<>(), new HashMap<>()); + return Arguments.makeNativeMap(constants); + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index a4905bc35dd4fe..6aec6901af480a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -154,4 +154,7 @@ public class ReactFeatureFlags { /** Disable the background executor for layout in Fabric */ public static boolean enableBackgroundExecutor = false; + + /** Use native view configs in bridgeless mode. */ + public static boolean useNativeViewConfigsInBridgelessMode = false; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProvider.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProvider.java new file mode 100644 index 00000000000000..77b95520c5c91a --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProvider.java @@ -0,0 +1,18 @@ +/* + * 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.uimanager; + +import com.facebook.proguard.annotations.DoNotStripAny; +import com.facebook.react.bridge.NativeMap; + +@DoNotStripAny +public interface UIConstantsProvider { + + /* Returns UIManager's constants. */ + NativeMap getConstants(); +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProviderManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProviderManager.java new file mode 100644 index 00000000000000..6479a0c1562d5e --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIConstantsProviderManager.java @@ -0,0 +1,41 @@ +/* + * 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.uimanager; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.proguard.annotations.DoNotStripAny; +import com.facebook.react.bridge.RuntimeExecutor; +import com.facebook.soloader.SoLoader; + +@DoNotStripAny +public class UIConstantsProviderManager { + + static { + staticInit(); + } + + @DoNotStrip + @SuppressWarnings("unused") + private final HybridData mHybridData; + + public UIConstantsProviderManager( + RuntimeExecutor runtimeExecutor, Object uiConstantsProviderManager) { + mHybridData = initHybrid(runtimeExecutor, uiConstantsProviderManager); + installJSIBindings(); + } + + private native HybridData initHybrid( + RuntimeExecutor runtimeExecutor, Object uiConstantsProviderManager); + + private native void installJSIBindings(); + + private static void staticInit() { + SoLoader.loadLibrary("uimanagerjni"); + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 78b174f0250429..9ff04bc2b07a4f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -229,7 +229,7 @@ private static Map createConstants(ViewManagerResolver viewManag } } - private static Map createConstants( + public static Map createConstants( List viewManagers, @Nullable Map customBubblingEvents, @Nullable Map customDirectEvents) { diff --git a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt index 4f862d383a5024..5420b01e5b7c2e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt @@ -101,6 +101,7 @@ add_react_common_subdir(react/nativemodule/core) add_react_common_subdir(jserrorhandler) add_react_common_subdir(react/bridgeless) add_react_common_subdir(react/bridgeless/hermes) +add_react_common_subdir(react/bridgeless/nativeviewconfig) # ReactAndroid JNI targets add_react_build_subdir(generated/source/codegen/jni) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/CMakeLists.txt index 3a3dbe33212521..8fc6ee71f0f13c 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/CMakeLists.txt @@ -19,6 +19,7 @@ target_link_libraries(uimanagerjni folly_runtime glog glog_init + bridgelessnativeviewconfig rrc_native yoga callinvokerholder diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/OnLoad.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/OnLoad.cpp index 4e153e824259c7..c7beb68a6acc8e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/OnLoad.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/OnLoad.cpp @@ -8,9 +8,11 @@ #include #include "ComponentNameResolverManager.h" +#include "UIConstantsProviderManager.h" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { return facebook::jni::initialize(vm, [] { facebook::react::ComponentNameResolverManager::registerNatives(); + facebook::react::UIConstantsProviderManager::registerNatives(); }); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.cpp new file mode 100644 index 00000000000000..42e8204a31bd5c --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.cpp @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#include + +#include +#include +#include +#include + +#include +#include "UIConstantsProviderManager.h" + +namespace facebook::react { + +using namespace facebook::jni; + +UIConstantsProviderManager::UIConstantsProviderManager( + jni::alias_ref jThis, + RuntimeExecutor runtimeExecutor, + jni::alias_ref uiConstantsProvider) + : javaPart_(jni::make_global(jThis)), + runtimeExecutor_(runtimeExecutor), + uiConstantsProvider_(jni::make_global(uiConstantsProvider)) {} + +jni::local_ref +UIConstantsProviderManager::initHybrid( + jni::alias_ref jThis, + jni::alias_ref runtimeExecutor, + jni::alias_ref uiConstantsProvider) { + return makeCxxInstance( + jThis, runtimeExecutor->cthis()->get(), uiConstantsProvider); +} + +void UIConstantsProviderManager::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", UIConstantsProviderManager::initHybrid), + makeNativeMethod( + "installJSIBindings", UIConstantsProviderManager::installJSIBindings), + }); +} + +void UIConstantsProviderManager::installJSIBindings() { + runtimeExecutor_([thizz = this](jsi::Runtime &runtime) { + auto uiConstantsProvider = [thizz, &runtime]() -> jsi::Value { + static auto getConstants = + jni::findClassStatic( + UIConstantsProviderManager::UIConstantsProviderJavaDescriptor) + ->getMethod()>( + "getConstants"); + auto constants = getConstants(thizz->uiConstantsProvider_.get()); + return jsi::valueFromDynamic(runtime, constants->cthis()->consume()); + }; + + LegacyUIManagerConstantsProviderBinding::install( + runtime, std::move(uiConstantsProvider)); + }); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.h b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.h new file mode 100644 index 00000000000000..ba537da6eb8b5f --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/jni/react/uimanager/UIConstantsProviderManager.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { + +class UIConstantsProviderManager + : public facebook::jni::HybridClass { + public: + static auto constexpr kJavaDescriptor = + "Lcom/facebook/react/uimanager/UIConstantsProviderManager;"; + + constexpr static auto UIConstantsProviderJavaDescriptor = + "com/facebook/react/uimanager/UIConstantsProvider"; + + static facebook::jni::local_ref initHybrid( + facebook::jni::alias_ref jThis, + facebook::jni::alias_ref runtimeExecutor, + facebook::jni::alias_ref uiConstantsProviderManager); + + static void registerNatives(); + + private: + friend HybridBase; + facebook::jni::global_ref javaPart_; + RuntimeExecutor runtimeExecutor_; + + facebook::jni::global_ref uiConstantsProvider_; + + void installJSIBindings(); + + explicit UIConstantsProviderManager( + facebook::jni::alias_ref jThis, + RuntimeExecutor runtimeExecutor, + facebook::jni::alias_ref uiConstantsProviderManager); +}; + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/bridgeless/nativeviewconfig/CMakeLists.txt b/packages/react-native/ReactCommon/react/bridgeless/nativeviewconfig/CMakeLists.txt new file mode 100644 index 00000000000000..68bd16e06e42ec --- /dev/null +++ b/packages/react-native/ReactCommon/react/bridgeless/nativeviewconfig/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options(-std=c++17) + +file(GLOB_RECURSE bridgeless_nativeviewconfig_SRC CONFIGURE_DEPENDS *.cpp) +add_library( + bridgelessnativeviewconfig + STATIC + ${bridgeless_nativeviewconfig_SRC} +) +target_include_directories(bridgelessnativeviewconfig PUBLIC .) + +target_link_libraries(bridgelessnativeviewconfig jsi)