From 08d87cdacc7201822eee5abcd37cf343f4c54d13 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Wed, 22 May 2019 13:11:53 -0700 Subject: [PATCH] Introduce ReactPackageTurboModuleManagerDelegate Summary: ## Summary People use `ReactPackage` instances to create NativeModules. To make the migration from NativeModule to TurboModule easy, I'm introducing a `TurboModuleManagerDelegate` that understands `ReactPackage`s, and uses them to lookup and create the Java TurboModule objects. This way, we don't have to change the way we declare NativeModules for the migration. ## TurboModule registration Each application should have its own subclass of `ReactPackageTurboModuleManagerDelegate`. This subclass is a hybrid class with a C++ and a Java part. The Java part can (and probably should) do nothing (for now). The C++ part has to implement the `moduleName -> jni::HostObject` and `moduleName, javaInstance -> jni::HostObject` functions for all TurboModules in the application. **Use Case: Migrating a NativeModule to TurboModule system** 1. Make the Java NativeModule extend `TurboModule`. (The reason why this doesn't happen automatically is probably because we haven't changed the Java codegen yet). 2. Modify the `moduleName -> jni::HostObject` or `moduleName, javaInstance -> jni::HostObject` functions to return the `TurboModule`. **Use Case: Adding a new TurboModule** 1. Add the TurboModule to a `ReactPackage` in the application. 2. Modify the `moduleName -> jni::HostObject` or `moduleName, javaInstance -> jni::HostObject` functions to return the TurboModule `jsi::HostObject`. **Note:** It's also possible to declare TurboModules by overriding the `getModule(String moduleName)` function of `ReactPackageTurboModuleManagerDelegate`. It's not a good idea, because it'll make switching between the NativeModule/TurboModule system difficult. Reviewed By: mdvacca Differential Revision: D15209129 fbshipit-source-id: 4b0a303595145be9b19d6f4934f956b91990f859 --- .../com/facebook/react/LazyReactPackage.java | 3 +- .../facebook/react/ReactInstanceManager.java | 4 + .../com/facebook/react/turbomodule/core/BUCK | 2 + ...eactPackageTurboModuleManagerDelegate.java | 104 ++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.java index 8c649f407f2..5c8217b1b93 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/LazyReactPackage.java @@ -76,8 +76,7 @@ public abstract class LazyReactPackage implements ReactPackage { * @param reactContext * @return */ - /* package */ - Iterable getNativeModuleIterator(final ReactApplicationContext reactContext) { + public Iterable getNativeModuleIterator(final ReactApplicationContext reactContext) { final Map reactModuleInfoMap = getReactModuleInfoProvider().getReactModuleInfos(); final List nativeModules = getNativeModules(reactContext); diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 022eb1cf58b..8c8bd4ceb6f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -307,6 +307,10 @@ public class ReactInstanceManager { return mMemoryPressureRouter; } + public List getPackages() { + return new ArrayList<>(mPackages); + } + private static void initializeSoLoaderIfNecessary(Context applicationContext) { // Call SoLoader.initialize here, this is required for apps that does not use exopackage and // does not use SoLoader for loading other native code except from the one used by React Native diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK index c6ad15fff0d..90437ec640b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK @@ -21,6 +21,8 @@ rn_android_library( react_native_target("java/com/facebook/debug/holder:holder"), react_native_target("java/com/facebook/react/bridge:interfaces"), react_native_target("java/com/facebook/react/bridge:bridge"), + react_native_target("java/com/facebook/react/config:config"), + react_native_target("java/com/facebook/react:react"), ":jscallinvokerholder", ], exported_deps = [ diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java new file mode 100644 index 00000000000..fab21c9fca3 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/ReactPackageTurboModuleManagerDelegate.java @@ -0,0 +1,104 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +package com.facebook.react.turbomodule.core; + +import com.facebook.react.ReactPackage; +import com.facebook.react.TurboReactPackage; +import com.facebook.react.bridge.CxxModuleWrapper; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; +import java.util.List; + +public abstract class ReactPackageTurboModuleManagerDelegate extends TurboModuleManagerDelegate { + private final List mPackages = new ArrayList<>(); + private final Map mModules = new HashMap<>(); + private final ReactApplicationContext mReactApplicationContext; + + public ReactPackageTurboModuleManagerDelegate(ReactApplicationContext reactApplicationContext, List packages) { + super(reactApplicationContext); + mReactApplicationContext = reactApplicationContext; + for (ReactPackage reactPackage : packages) { + if (reactPackage instanceof TurboReactPackage) { + mPackages.add((TurboReactPackage)reactPackage); + } + } + } + + @Nullable + @Override + public TurboModule getModule(String moduleName) { + TurboModule module = resolveModule(moduleName); + if (module == null) { + return null; + } + + if (module instanceof CxxModuleWrapper) { + return null; + } + + + return module; + } + + @Nullable + @Override + public CxxModuleWrapper getLegacyCxxModule(String moduleName) { + TurboModule module = resolveModule(moduleName); + if (module == null) { + return null; + } + + if (!(module instanceof CxxModuleWrapper)) { + return null; + } + + + return (CxxModuleWrapper)module; + } + + private TurboModule resolveModule(String moduleName) { + if (mModules.containsKey(moduleName)) { + return mModules.get(moduleName); + } + + NativeModule resolvedModule = null; + + for (final TurboReactPackage pkg : mPackages) { + try { + NativeModule module = pkg.getModule(moduleName, mReactApplicationContext); + if (resolvedModule == null || module != null && module.canOverrideExistingModule()) { + resolvedModule = module; + } + } catch (IllegalArgumentException ex) { + /** + * TurboReactPackages can throw an IllegalArgumentException when a module + * isn't found. If this happens, it's safe to ignore the exception because + * a later TurboReactPackage could provide the module. + */ + } + } + + if (resolvedModule instanceof TurboModule) { + mModules.put(moduleName, (TurboModule)resolvedModule); + } else { + /** + * 1. The list of TurboReactPackages doesn't change. + * 2. TurboReactPackage.getModule is deterministic. Therefore, any two + * invocations of TurboReactPackage.getModule will return the same result + * given that they're provided the same arguments. + * + * Hence, if module lookup fails once, we know it'll fail every time. + * Therefore, we can write null to the mModules Map and avoid doing this + * extra work. + */ + mModules.put(moduleName, null); + } + + return mModules.get(moduleName); + } +}