From 12c0ec85f7fd116b2840ecd4e2edbd57de7638ef Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Wed, 22 May 2019 13:11:53 -0700 Subject: [PATCH] Implement ReactContext.getNativeModule support Summary: `ReactContext.getNativeModule` can be used to access NativeModules. With these changes, it can also be used to instantiate (if necessary) and retrieve a TurboModule. Reviewed By: mdvacca Differential Revision: D15167631 fbshipit-source-id: 3cb0d9a4be16cbadebbf6648c3f1481ba26513c3 --- .../react/bridge/CatalystInstance.java | 9 +++++ .../react/bridge/CatalystInstanceImpl.java | 33 ++++++++++++--- .../com/facebook/react/turbomodule/core/BUCK | 4 +- .../turbomodule/core/TurboModuleManager.java | 40 ++++++++++++++----- .../react/turbomodule/core/interfaces/BUCK | 5 ++- .../core/interfaces/TurboModuleRegistry.java | 35 ++++++++++++++++ 6 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModuleRegistry.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java index c7b6ced623d..69751f22508 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -11,6 +11,7 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.queue.ReactQueueConfiguration; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.turbomodule.core.interfaces.JSCallInvokerHolder; +import com.facebook.react.turbomodule.core.interfaces.TurboModuleRegistry; import java.util.Collection; import java.util.List; import javax.annotation.Nullable; @@ -110,4 +111,12 @@ public interface CatalystInstance * Required for TurboModuleManager initialization. */ JSCallInvokerHolder getJSCallInvokerHolder(); + + /** + * For the time being, we want code relying on the old infra to also + * work with TurboModules. Hence, we must provide the TurboModuleRegistry + * to CatalystInstance so that getNativeModule, hasNativeModule, and + * getNativeModules can also return TurboModules. + */ + void setTurboModuleRegistry(TurboModuleRegistry getter); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java index b2325fdcb86..1933f10ae70 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java @@ -25,10 +25,10 @@ import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.turbomodule.core.JSCallInvokerHolderImpl; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import com.facebook.react.turbomodule.core.interfaces.TurboModuleRegistry; import com.facebook.systrace.Systrace; import com.facebook.systrace.TraceListener; -import java.lang.annotation.Annotation; -import java.lang.annotation.Native; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; @@ -96,6 +96,7 @@ public class CatalystInstanceImpl implements CatalystInstance { private @Nullable String mSourceURL; private JavaScriptContextHolder mJavaScriptContextHolder; + private @Nullable TurboModuleRegistry mTurboModuleRegistry = null; // C++ parts private final HybridData mHybridData; @@ -421,16 +422,25 @@ public class CatalystInstanceImpl implements CatalystInstance { @Override public boolean hasNativeModule(Class nativeModuleInterface) { - return mNativeModuleRegistry.hasModule(getNameFromAnnotation(nativeModuleInterface)); + String moduleName = getNameFromAnnotation(nativeModuleInterface); + return mTurboModuleRegistry != null && mTurboModuleRegistry.hasModule(moduleName) ? true : mNativeModuleRegistry.hasModule(moduleName); } @Override public T getNativeModule(Class nativeModuleInterface) { - return (T) mNativeModuleRegistry.getModule(getNameFromAnnotation(nativeModuleInterface)); + return (T) getNativeModule(getNameFromAnnotation(nativeModuleInterface)); } @Override public NativeModule getNativeModule(String moduleName) { + if (mTurboModuleRegistry != null) { + TurboModule turboModule = mTurboModuleRegistry.getModule(moduleName); + + if (turboModule != null) { + return (NativeModule)turboModule; + } + } + return mNativeModuleRegistry.getModule(moduleName); } @@ -445,7 +455,16 @@ public class CatalystInstanceImpl implements CatalystInstance { // This is only used by com.facebook.react.modules.common.ModuleDataCleaner @Override public Collection getNativeModules() { - return mNativeModuleRegistry.getAllModules(); + Collection nativeModules = new ArrayList<>(); + nativeModules.addAll(mNativeModuleRegistry.getAllModules()); + + if (mTurboModuleRegistry != null) { + for (TurboModule turboModule : mTurboModuleRegistry.getModules()) { + nativeModules.add((NativeModule) turboModule); + } + } + + return nativeModules; } private native void jniHandleMemoryPressure(int level); @@ -517,6 +536,10 @@ public class CatalystInstanceImpl implements CatalystInstance { } } + public void setTurboModuleRegistry(TurboModuleRegistry getter) { + mTurboModuleRegistry = getter; + } + private void decrementPendingJSCalls() { int newPendingCalls = mPendingJSCalls.decrementAndGet(); // TODO(9604406): handle case of web workers injecting messages to main thread 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 aa8b05cf754..c6ad15fff0d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/BUCK @@ -13,7 +13,6 @@ rn_android_library( "PUBLIC", ], deps = [ - react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"), react_native_dep("java/com/facebook/systrace:systrace"), react_native_dep("libraries/soloader/java/com/facebook/soloader:soloader"), react_native_dep("third-party/java/infer-annotations:infer-annotations"), @@ -24,6 +23,9 @@ rn_android_library( react_native_target("java/com/facebook/react/bridge:bridge"), ":jscallinvokerholder", ], + exported_deps = [ + react_native_target("java/com/facebook/react/turbomodule/core/interfaces:interfaces"), + ], ) rn_android_library( diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java index 158ff5bd298..511dd690f31 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/TurboModuleManager.java @@ -9,20 +9,24 @@ package com.facebook.react.turbomodule.core; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.CatalystInstance; import com.facebook.react.bridge.JSIModule; import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.queue.MessageQueueThread; import com.facebook.react.turbomodule.core.interfaces.JSCallInvokerHolder; import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import com.facebook.react.turbomodule.core.interfaces.TurboModuleRegistry; import com.facebook.soloader.SoLoader; +import java.util.*; +import javax.annotation.Nullable; /** * This is the main class and entry point for TurboModules. * Note that this is a hybrid class, and has a C++ counterpart * This class installs the JSI bindings. It also implements the method to get a Java module, that the C++ counterpart calls. */ -public class TurboModuleManager implements JSIModule { +public class TurboModuleManager implements JSIModule, TurboModuleRegistry { static { SoLoader.loadLibrary("turbomodulejsijni"); } @@ -30,6 +34,8 @@ public class TurboModuleManager implements JSIModule { private final ReactApplicationContext mReactApplicationContext; private final TurboModuleManagerDelegate mTurbomoduleManagerDelegate; + private final Map mTurboModules = new HashMap<>(); + @DoNotStrip @SuppressWarnings("unused") private final HybridData mHybridData; @@ -41,9 +47,30 @@ public class TurboModuleManager implements JSIModule { mTurbomoduleManagerDelegate = tmmDelegate; } @DoNotStrip - @SuppressWarnings("unused") - private TurboModule getJavaModule(String name) { - return mTurbomoduleManagerDelegate.getModule(name); + @Nullable + protected TurboModule getJavaModule(String name) { + if (!mTurboModules.containsKey(name)) { + final TurboModule turboModule = mTurbomoduleManagerDelegate.getModule(name); + + if (turboModule != null) { + mTurboModules.put(name, turboModule); + } + } + + return mTurboModules.get(name); + } + + @Nullable + public TurboModule getModule(String name) { + return getJavaModule(name); + } + + public Collection getModules() { + return mTurboModules.values(); + } + + public boolean hasModule(String name) { + return mTurboModules.containsKey(name); } private native HybridData initHybrid(long jsContext, JSCallInvokerHolderImpl jsQueue, TurboModuleManagerDelegate tmmDelegate); @@ -59,9 +86,4 @@ public class TurboModuleManager implements JSIModule { @Override public void onCatalystInstanceDestroy() {} - - /** All applications must implement this interface, and provide the Java TurboModule class */ - public interface ModuleProvider { - TurboModule getModule(String name, ReactApplicationContext reactApplicationContext); - } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK index d18e484bfb0..fac8ba723f9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/BUCK @@ -1,4 +1,4 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "rn_android_library") +load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "rn_android_library") rn_android_library( name = "interfaces", @@ -9,4 +9,7 @@ rn_android_library( visibility = [ "PUBLIC", ], + deps = [ + react_native_dep("third-party/java/jsr-305:jsr-305"), + ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModuleRegistry.java b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModuleRegistry.java new file mode 100644 index 00000000000..85794679bb4 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/interfaces/TurboModuleRegistry.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its 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.turbomodule.core.interfaces; + +import java.util.Collection; +import javax.annotation.Nullable; + +/** + * Interface to allow for creating and retrieving TurboModuels. + */ +public interface TurboModuleRegistry { + + /** + * Return the TurboModule instance that has that name `moduleName`. + * If the `moduleName` TurboModule hasn't been instantiated, instantiate it. + * If no TurboModule is registered under `moduleName`, return null. + */ + @Nullable + TurboModule getModule(String moduleName); + + /** + * Get all instantiated TurboModules. + */ + Collection getModules(); + + /** + * Has the TurboModule with name `moduleName` been instantiated? + */ + boolean hasModule(String moduleName); +}