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); + } +}