// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.cxxbridge;
import javax.annotation.Nullable;
import javax.inject.Provider;
import java.util.concurrent.ExecutionException;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.common.futures.SimpleSettableFuture;
import com.facebook.react.module.model.Info;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.systrace.Systrace;
import com.facebook.systrace.SystraceMessage;
import static com.facebook.infer.annotation.Assertions.assertNotNull;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_MODULE_END;
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_MODULE_START;
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
/**
* Holder to enable us to lazy create native modules.
*
* This works by taking a provider instead of an instance, when it is first required we'll create
* and initialize it. Initialization currently always happens on the UI thread but this is due to
* change for performance reasons.
*
* Lifecycle events via a {@link LifecycleEventListener} will still always happen on the UI thread.
*/
public class ModuleHolder {
private final Info mInfo;
private @Nullable Provider<? extends NativeModule> mProvider;
private @Nullable NativeModule mModule;
private boolean mInitializeNeeded;
public ModuleHolder(
Class<? extends NativeModule> clazz,
@Nullable ReactModuleInfo reactModuleInfo,
Provider<? extends NativeModule> provider) {
mInfo = reactModuleInfo == null ? new LegacyModuleInfo(clazz) : reactModuleInfo;
mProvider = provider;
if (mInfo.needsEagerInit()) {
mModule = doCreate();
}
}
public synchronized void initialize() {
if (mModule != null) {
doInitialize(mModule);
} else {
mInitializeNeeded = true;
}
}
public synchronized void destroy() {
if (mModule != null) {
mModule.onCatalystInstanceDestroy();
}
}
public Info getInfo() {
return mInfo;
}
public synchronized NativeModule getModule() {
if (mModule == null) {
mModule = doCreate();
}
return mModule;
}
private NativeModule doCreate() {
NativeModule module = create();
mProvider = null;
return module;
}
private NativeModule create() {
boolean isEagerModule = mInfo instanceof LegacyModuleInfo;
String name = isEagerModule ? ((LegacyModuleInfo) mInfo).mType.getSimpleName() : mInfo.name();
if (!isEagerModule) {
ReactMarker.logMarker(CREATE_MODULE_START);
}
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createModule")
.arg("name", name)
.flush();
NativeModule module = assertNotNull(mProvider).get();
if (mInitializeNeeded) {
doInitialize(module);
mInitializeNeeded = false;
}
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
if (!isEagerModule) {
ReactMarker.logMarker(CREATE_MODULE_END);
}
return module;
}
private void doInitialize(NativeModule module) {
SystraceMessage.Builder section =
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initialize");
if (module instanceof CxxModuleWrapper) {
section.arg("className", module.getClass().getSimpleName());
} else {
section.arg("name", mInfo.name());
}
section.flush();
callInitializeOnUiThread(module);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
// TODO(t11394264): Use the native module thread here after the old bridge is gone
private static void callInitializeOnUiThread(final NativeModule module) {
if (UiThreadUtil.isOnUiThread()) {
module.initialize();
return;
}
final SimpleSettableFuture<Void> future = new SimpleSettableFuture<>();
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeOnUiThread");
try {
module.initialize();
future.set(null);
} catch (Exception e) {
future.setException(e);
}
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
});
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
private class LegacyModuleInfo implements Info {
public final Class<?> mType;
public LegacyModuleInfo(Class<?> type) {
mType = type;
}
public String name() {
return getModule().getName();
}
public boolean canOverrideExistingModule() {
return getModule().canOverrideExistingModule();
}
public boolean supportsWebWorkers() {
return getModule().supportsWebWorkers();
}
public boolean needsEagerInit() {
return true;
}
}
}