Skip to content

Commit e622d51

Browse files
mhorowitzfacebook-github-bot
authored andcommitted
Support ModuleHolder-based lazy init of C++ modules with C++ bridge on android
Reviewed By: AaaChiuuu Differential Revision: D4614479 fbshipit-source-id: 109ac34b8688f0113675e4a4479d1ddcc6169ed4
1 parent 6410e25 commit e622d51

20 files changed

+174
-52
lines changed

React/CxxBridge/RCTCxxBridge.mm

+2-1
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,8 @@ - (BOOL)moduleIsInitialized:(Class)moduleClass
553553
}
554554
modules.emplace_back(
555555
new QueueNativeModule(self, std::make_unique<CxxNativeModule>(
556-
_reactInstance, [(RCTCxxModule *)(moduleData.instance) move])));
556+
_reactInstance, [moduleData.name UTF8String],
557+
[moduleData] { return [(RCTCxxModule *)(moduleData.instance) move]; })));
557558
} else {
558559
modules.emplace_back(new RCTNativeModule(self, moduleData));
559560
}

ReactAndroid/src/main/java/com/facebook/react/cxxbridge/CatalystInstanceImpl.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private CatalystInstanceImpl(
110110
final JavaScriptModuleRegistry jsModuleRegistry,
111111
final JSBundleLoader jsBundleLoader,
112112
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
113-
FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
113+
FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge.");
114114
mHybridData = initHybrid();
115115

116116
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
@@ -123,13 +123,15 @@ private CatalystInstanceImpl(
123123
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
124124
mTraceListener = new JSProfilerTraceListener(this);
125125

126+
FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
126127
initializeBridge(
127128
new BridgeCallback(this),
128129
jsExecutor,
129130
mReactQueueConfiguration.getJSQueueThread(),
130131
mReactQueueConfiguration.getNativeModulesQueueThread(),
131132
mJavaRegistry.getJavaModules(this),
132133
mJavaRegistry.getCxxModules());
134+
FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
133135
mMainExecutorToken = getMainExecutorToken();
134136
}
135137

@@ -182,7 +184,7 @@ private native void initializeBridge(
182184
MessageQueueThread jsQueue,
183185
MessageQueueThread moduleQueue,
184186
Collection<JavaModuleWrapper> javaModules,
185-
Collection<CxxModuleWrapper> cxxModules);
187+
Collection<ModuleHolder> cxxModules);
186188

187189
/**
188190
* This API is used in situations where the JS bundle is being executed not on

ReactAndroid/src/main/java/com/facebook/react/cxxbridge/NativeModuleRegistry.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ public NativeModuleRegistry(
5353
return javaModules;
5454
}
5555

56-
/* package */ Collection<CxxModuleWrapper> getCxxModules() {
57-
ArrayList<CxxModuleWrapper> cxxModules = new ArrayList<>();
56+
/* package */ Collection<ModuleHolder> getCxxModules() {
57+
ArrayList<ModuleHolder> cxxModules = new ArrayList<>();
5858
for (Map.Entry<Class<? extends NativeModule>, ModuleHolder> entry : mModules.entrySet()) {
5959
Class<?> type = entry.getKey();
6060
if (CxxModuleWrapper.class.isAssignableFrom(type)) {
61-
cxxModules.add((CxxModuleWrapper) entry.getValue().getModule());
61+
cxxModules.add(entry.getValue());
6262
}
6363
}
6464
return cxxModules;

ReactAndroid/src/main/jni/xreact/jni/Android.mk

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ LOCAL_SRC_FILES := \
1515
JSLogging.cpp \
1616
JniJSModulesUnbundle.cpp \
1717
MethodInvoker.cpp \
18+
ModuleRegistryBuilder.cpp \
1819
NativeArray.cpp \
1920
NativeCommon.cpp \
2021
NativeMap.cpp \

ReactAndroid/src/main/jni/xreact/jni/BUCK

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ EXPORTED_HEADERS = [
66
"JExecutorToken.h",
77
"JSLoader.h",
88
"MethodInvoker.h",
9+
"ModuleRegistryBuilder.h",
910
"NativeArray.h",
1011
"NativeCommon.h",
1112
"NativeMap.h",

ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.cpp

+4-13
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <cxxreact/ModuleRegistry.h>
2121
#include <cxxreact/CxxNativeModule.h>
2222

23+
#include "CxxModuleWrapper.h"
2324
#include "JavaScriptExecutorHolder.h"
2425
#include "JniJSModulesUnbundle.h"
2526
#include "JNativeRunnable.h"
@@ -128,21 +129,10 @@ void CatalystInstanceImpl::initializeBridge(
128129
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
129130
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
130131
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
131-
jni::alias_ref<jni::JCollection<CxxModuleWrapper::javaobject>::javaobject> cxxModules) {
132+
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
132133
// TODO mhorowitz: how to assert here?
133134
// Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
134135

135-
std::vector<std::unique_ptr<NativeModule>> modules;
136-
std::weak_ptr<Instance> winstance(instance_);
137-
for (const auto& jm : *javaModules) {
138-
modules.emplace_back(folly::make_unique<JavaNativeModule>(winstance, jm));
139-
}
140-
for (const auto& cm : *cxxModules) {
141-
modules.emplace_back(
142-
folly::make_unique<CxxNativeModule>(winstance, std::move(cthis(cm)->getModule())));
143-
}
144-
auto moduleRegistry = std::make_shared<ModuleRegistry>(std::move(modules));
145-
146136
// This used to be:
147137
//
148138
// Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback
@@ -163,7 +153,8 @@ void CatalystInstanceImpl::initializeBridge(
163153
jseh->getExecutorFactory(),
164154
folly::make_unique<JMessageQueueThread>(jsQueue),
165155
folly::make_unique<JMessageQueueThread>(moduleQueue),
166-
moduleRegistry);
156+
buildModuleRegistry(std::weak_ptr<Instance>(instance_),
157+
javaModules, cxxModules));
167158
}
168159

169160
void CatalystInstanceImpl::jniSetSourceURL(const std::string& sourceURL) {

ReactAndroid/src/main/jni/xreact/jni/CatalystInstanceImpl.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "JMessageQueueThread.h"
1111
#include "JSLoader.h"
1212
#include "JavaModuleWrapper.h"
13+
#include "ModuleRegistryBuilder.h"
1314

1415
namespace facebook {
1516
namespace react {
@@ -48,7 +49,7 @@ class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
4849
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
4950
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
5051
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
51-
jni::alias_ref<jni::JCollection<CxxModuleWrapper::javaobject>::javaobject> cxxModules);
52+
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules);
5253

5354
/**
5455
* Sets the source URL of the underlying bridge without loading any JS code.

ReactAndroid/src/main/jni/xreact/jni/CxxModuleWrapper.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
namespace facebook {
1313
namespace react {
1414

15-
class CxxModuleWrapper : public jni::HybridClass<CxxModuleWrapper> {
15+
struct JNativeModule : jni::JavaClass<JNativeModule> {
16+
constexpr static const char *const kJavaDescriptor =
17+
"Lcom/facebook/react/bridge/NativeModule;";
18+
};
19+
20+
class CxxModuleWrapper :
21+
public jni::HybridClass<CxxModuleWrapper, JNativeModule> {
1622
public:
1723
constexpr static const char *const kJavaDescriptor =
1824
"Lcom/facebook/react/cxxbridge/CxxModuleWrapper;";

ReactAndroid/src/main/jni/xreact/jni/JavaModuleWrapper.h

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct JavaModuleWrapper : jni::JavaClass<JavaModuleWrapper> {
2727
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/cxxbridge/JavaModuleWrapper;";
2828

2929
jni::local_ref<JBaseJavaModule::javaobject> getModule() {
30+
// This is the call which causes a lazy Java module to actually be
31+
// created.
3032
static auto getModule = javaClassStatic()->getMethod<JBaseJavaModule::javaobject()>("getModule");
3133
return getModule(self());
3234
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#include "ModuleRegistryBuilder.h"
4+
5+
#include <cxxreact/CxxNativeModule.h>
6+
#include <folly/Memory.h>
7+
8+
namespace facebook {
9+
namespace react {
10+
11+
std::string ModuleHolder::getName() const {
12+
static auto method = getClass()->getMethod<jstring()>("getName");
13+
return method(self())->toStdString();
14+
}
15+
16+
xplat::module::CxxModule::Provider ModuleHolder::getProvider() const {
17+
return [self=jni::make_global(self())] {
18+
static auto method =
19+
ModuleHolder::javaClassStatic()->getMethod<JNativeModule::javaobject()>(
20+
"getModule");
21+
// This is the call which uses the lazy Java Provider to instantiate the
22+
// Java CxxModuleWrapper which contains the CxxModule.
23+
auto module = method(self);
24+
CHECK(module->isInstanceOf(CxxModuleWrapper::javaClassStatic()))
25+
<< "module isn't a C++ module";
26+
auto cxxModule = jni::static_ref_cast<CxxModuleWrapper::javaobject>(module);
27+
// Then, we grab the CxxModule from the wrapper, which is no longer needed.
28+
return cxxModule->cthis()->getModule();
29+
};
30+
}
31+
32+
std::unique_ptr<ModuleRegistry> buildModuleRegistry(
33+
std::weak_ptr<Instance> winstance,
34+
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
35+
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
36+
std::vector<std::unique_ptr<NativeModule>> modules;
37+
for (const auto& jm : *javaModules) {
38+
modules.emplace_back(folly::make_unique<JavaNativeModule>(winstance, jm));
39+
}
40+
for (const auto& cm : *cxxModules) {
41+
modules.emplace_back(
42+
folly::make_unique<CxxNativeModule>(winstance, cm->getName(), cm->getProvider()));
43+
}
44+
if (modules.empty()) {
45+
return nullptr;
46+
} else {
47+
return folly::make_unique<ModuleRegistry>(std::move(modules));
48+
}
49+
}
50+
51+
}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2004-present Facebook. All Rights Reserved.
2+
3+
#include <string>
4+
5+
#include <cxxreact/CxxModule.h>
6+
#include <cxxreact/ModuleRegistry.h>
7+
#include <fb/fbjni.h>
8+
9+
#include "CxxModuleWrapper.h"
10+
#include "JavaModuleWrapper.h"
11+
12+
namespace facebook {
13+
namespace react {
14+
15+
class ModuleHolder : public jni::JavaClass<ModuleHolder> {
16+
public:
17+
static auto constexpr kJavaDescriptor =
18+
"Lcom/facebook/react/cxxbridge/ModuleHolder;";
19+
20+
std::string getName() const;
21+
xplat::module::CxxModule::Provider getProvider() const;
22+
};
23+
24+
std::unique_ptr<ModuleRegistry> buildModuleRegistry(
25+
std::weak_ptr<Instance> winstance,
26+
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
27+
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules);
28+
29+
}
30+
}

ReactCommon/cxxreact/Android.mk

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ include $(CLEAR_VARS)
55
LOCAL_MODULE := libreactnativefb
66

77
LOCAL_SRC_FILES := \
8+
CxxMessageQueue.cpp \
89
CxxNativeModule.cpp \
910
Instance.cpp \
1011
JSCExecutor.cpp \

ReactCommon/cxxreact/CxxModule.h

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class CxxModule {
5050
class SyncTagType {};
5151

5252
public:
53+
typedef std::function<std::unique_ptr<CxxModule>()> Provider;
54+
5355
typedef std::function<void(std::vector<folly::dynamic>)> Callback;
5456

5557
constexpr static AsyncTagType AsyncTag = AsyncTagType();

ReactCommon/cxxreact/CxxNativeModule.cpp

+19-6
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,19 @@ CxxModule::Callback convertCallback(
4747
}
4848

4949
CxxNativeModule::CxxNativeModule(std::weak_ptr<Instance> instance,
50-
std::unique_ptr<CxxModule> module)
50+
std::string name,
51+
CxxModule::Provider provider)
5152
: instance_(instance)
52-
, module_(std::move(module))
53-
, methods_(module_->getMethods()) {
54-
module_->setInstance(instance);
55-
}
53+
, name_(std::move(name))
54+
, provider_(provider) {}
5655

5756
std::string CxxNativeModule::getName() {
58-
return module_->getName();
57+
return name_;
5958
}
6059

6160
std::vector<MethodDescriptor> CxxNativeModule::getMethods() {
61+
lazyInit();
62+
6263
std::vector<MethodDescriptor> descs;
6364
for (auto& method : methods_) {
6465
assert(method.func || method.syncFunc);
@@ -68,6 +69,8 @@ std::vector<MethodDescriptor> CxxNativeModule::getMethods() {
6869
}
6970

7071
folly::dynamic CxxNativeModule::getConstants() {
72+
lazyInit();
73+
7174
folly::dynamic constants = folly::dynamic::object();
7275
for (auto& pair : module_->getConstants()) {
7376
constants.insert(std::move(pair.first), std::move(pair.second));
@@ -168,5 +171,15 @@ MethodCallResult CxxNativeModule::callSerializableNativeHook(
168171
return method.syncFunc(std::move(args));
169172
}
170173

174+
void CxxNativeModule::lazyInit() {
175+
if (module_) {
176+
return;
177+
}
178+
179+
module_ = provider_();
180+
methods_ = module_->getMethods();
181+
module_->setInstance(instance_);
182+
}
183+
171184
}
172185
}

ReactCommon/cxxreact/CxxNativeModule.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ std::function<void(folly::dynamic)> makeCallback(
1515

1616
class CxxNativeModule : public NativeModule {
1717
public:
18-
CxxNativeModule(std::weak_ptr<Instance> instance,
19-
std::unique_ptr<xplat::module::CxxModule> module);
18+
CxxNativeModule(std::weak_ptr<Instance> instance, std::string name,
19+
xplat::module::CxxModule::Provider provider);
2020

2121
std::string getName() override;
2222
std::vector<MethodDescriptor> getMethods() override;
@@ -27,7 +27,11 @@ class CxxNativeModule : public NativeModule {
2727
ExecutorToken token, unsigned int hookId, folly::dynamic&& args) override;
2828

2929
private:
30+
void lazyInit();
31+
3032
std::weak_ptr<Instance> instance_;
33+
std::string name_;
34+
xplat::module::CxxModule::Provider provider_;
3135
std::unique_ptr<xplat::module::CxxModule> module_;
3236
std::vector<xplat::module::CxxModule::Method> methods_;
3337
};

ReactCommon/cxxreact/Instance.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void Instance::initializeBridge(
4040
if (!nativeQueue) {
4141
// TODO pass down a thread/queue from java, instead of creating our own.
4242

43-
auto queue = std::make_unique<CxxMessageQueue>();
43+
auto queue = folly::make_unique<CxxMessageQueue>();
4444
std::thread t(queue->getUnregisteredRunLoop());
4545
t.detach();
4646
nativeQueue = std::move(queue);

0 commit comments

Comments
 (0)