mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
4cf70306b9
Summary: Due to the way (initial) surface implementation plays with ReactRootView (RRV), the react tag from the UIManagerModule is now set on the surface container view rather than on RRV as it was historically done. RRV still caches the react tag but just doesn't use it as an id on the view so `RRV.getViewId()` no longer equals `RRV.getReactTag()`. Reviewed By: mdvacca Differential Revision: D14110104 fbshipit-source-id: 6dbcc41c85fd7a6c32c7250f68f4a84bed4e075a
1220 lines
48 KiB
Java
1220 lines
48 KiB
Java
/**
|
|
* 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;
|
|
|
|
import static com.facebook.infer.annotation.ThreadConfined.UI;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.ATTACH_MEASURED_ROOT_VIEWS_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.ATTACH_MEASURED_ROOT_VIEWS_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_NATIVE_MODULE_REGISTRY_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_NATIVE_MODULE_REGISTRY_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_CATALYST_INSTANCE_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_CATALYST_INSTANCE_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_REACT_CONTEXT_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_VIEW_MANAGERS_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_VIEW_MANAGERS_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.PRE_SETUP_REACT_CONTEXT_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.PRE_SETUP_REACT_CONTEXT_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.PROCESS_PACKAGES_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.PROCESS_PACKAGES_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.REACT_CONTEXT_THREAD_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.REACT_CONTEXT_THREAD_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.SETUP_REACT_CONTEXT_END;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.SETUP_REACT_CONTEXT_START;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.CHANGE_THREAD_PRIORITY;
|
|
import static com.facebook.react.bridge.ReactMarkerConstants.VM_INIT;
|
|
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
|
|
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_APPS;
|
|
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
|
|
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JS_VM_CALLS;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.os.Process;
|
|
import android.support.v4.view.ViewCompat;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
import com.facebook.common.logging.FLog;
|
|
import com.facebook.debug.holder.PrinterHolder;
|
|
import com.facebook.debug.tags.ReactDebugOverlayTags;
|
|
import com.facebook.infer.annotation.Assertions;
|
|
import com.facebook.infer.annotation.ThreadConfined;
|
|
import com.facebook.infer.annotation.ThreadSafe;
|
|
import com.facebook.react.bridge.Arguments;
|
|
import com.facebook.react.bridge.CatalystInstance;
|
|
import com.facebook.react.bridge.CatalystInstanceImpl;
|
|
import com.facebook.react.bridge.JSBundleLoader;
|
|
import com.facebook.react.bridge.JSIModulePackage;
|
|
import com.facebook.react.bridge.JavaJSExecutor;
|
|
import com.facebook.react.bridge.JavaScriptExecutor;
|
|
import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
|
import com.facebook.react.bridge.NativeDeltaClient;
|
|
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
|
import com.facebook.react.bridge.NativeModuleRegistry;
|
|
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener;
|
|
import com.facebook.react.bridge.ProxyJavaScriptExecutor;
|
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
import com.facebook.react.bridge.ReactContext;
|
|
import com.facebook.react.bridge.ReactMarker;
|
|
import com.facebook.react.bridge.ReactMarkerConstants;
|
|
import com.facebook.react.bridge.UIManager;
|
|
import com.facebook.react.bridge.UiThreadUtil;
|
|
import com.facebook.react.bridge.WritableNativeMap;
|
|
import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec;
|
|
import com.facebook.react.common.LifecycleState;
|
|
import com.facebook.react.common.ReactConstants;
|
|
import com.facebook.react.common.annotations.VisibleForTesting;
|
|
import com.facebook.react.devsupport.DevSupportManagerFactory;
|
|
import com.facebook.react.devsupport.ReactInstanceManagerDevHelper;
|
|
import com.facebook.react.devsupport.RedBoxHandler;
|
|
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
|
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
|
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
|
|
import com.facebook.react.modules.appregistry.AppRegistry;
|
|
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
import com.facebook.react.modules.core.ReactChoreographer;
|
|
import com.facebook.react.modules.debug.interfaces.DeveloperSettings;
|
|
import com.facebook.react.modules.fabric.ReactFabric;
|
|
import com.facebook.react.packagerconnection.RequestHandler;
|
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
|
import com.facebook.react.uimanager.UIImplementationProvider;
|
|
import com.facebook.react.uimanager.UIManagerHelper;
|
|
import com.facebook.react.uimanager.ViewManager;
|
|
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
|
import com.facebook.soloader.SoLoader;
|
|
import com.facebook.systrace.Systrace;
|
|
import com.facebook.systrace.SystraceMessage;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import javax.annotation.Nullable;
|
|
|
|
/**
|
|
* This class is managing instances of {@link CatalystInstance}. It exposes a way to configure
|
|
* catalyst instance using {@link ReactPackage} and keeps track of the lifecycle of that
|
|
* instance. It also sets up connection between the instance and developers support functionality
|
|
* of the framework.
|
|
*
|
|
* An instance of this manager is required to start JS application in {@link ReactRootView} (see
|
|
* {@link ReactRootView#startReactApplication} for more info).
|
|
*
|
|
* The lifecycle of the instance of {@link ReactInstanceManager} should be bound to the
|
|
* activity that owns the {@link ReactRootView} that is used to render react application using this
|
|
* instance manager (see {@link ReactRootView#startReactApplication}). It's required to pass owning
|
|
* activity's lifecycle events to the instance manager (see {@link #onHostPause}, {@link
|
|
* #onHostDestroy} and {@link #onHostResume}).
|
|
*
|
|
* To instantiate an instance of this class use {@link #builder}.
|
|
*/
|
|
@ThreadSafe
|
|
public class ReactInstanceManager {
|
|
|
|
private static final String TAG = ReactInstanceManager.class.getSimpleName();
|
|
/**
|
|
* Listener interface for react instance events.
|
|
*/
|
|
public interface ReactInstanceEventListener {
|
|
|
|
/**
|
|
* Called when the react context is initialized (all modules registered). Always called on the
|
|
* UI thread.
|
|
*/
|
|
void onReactContextInitialized(ReactContext context);
|
|
}
|
|
|
|
private final Set<ReactRootView> mAttachedRootViews = Collections.synchronizedSet(
|
|
new HashSet<ReactRootView>());
|
|
|
|
private volatile LifecycleState mLifecycleState;
|
|
|
|
private @Nullable @ThreadConfined(UI) ReactContextInitParams mPendingReactContextInitParams;
|
|
private volatile @Nullable Thread mCreateReactContextThread;
|
|
/* accessed from any thread */
|
|
private final JavaScriptExecutorFactory mJavaScriptExecutorFactory;
|
|
|
|
private final @Nullable JSBundleLoader mBundleLoader;
|
|
private final @Nullable String mJSMainModulePath; /* path to JS bundle root on packager server */
|
|
private final List<ReactPackage> mPackages;
|
|
private final DevSupportManager mDevSupportManager;
|
|
private final boolean mUseDeveloperSupport;
|
|
private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener;
|
|
private final Object mReactContextLock = new Object();
|
|
private @Nullable volatile ReactContext mCurrentReactContext;
|
|
private final Context mApplicationContext;
|
|
private @Nullable @ThreadConfined(UI) DefaultHardwareBackBtnHandler mDefaultBackButtonImpl;
|
|
private @Nullable Activity mCurrentActivity;
|
|
private final Collection<ReactInstanceEventListener> mReactInstanceEventListeners =
|
|
Collections.synchronizedSet(new HashSet<ReactInstanceEventListener>());
|
|
// Identifies whether the instance manager is or soon will be initialized (on background thread)
|
|
private volatile boolean mHasStartedCreatingInitialContext = false;
|
|
// Identifies whether the instance manager destroy function is in process,
|
|
// while true any spawned create thread should wait for proper clean up before initializing
|
|
private volatile Boolean mHasStartedDestroying = false;
|
|
private final MemoryPressureRouter mMemoryPressureRouter;
|
|
private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
|
|
private final @Nullable JSIModulePackage mJSIModulePackage;
|
|
private List<ViewManager> mViewManagers;
|
|
|
|
private class ReactContextInitParams {
|
|
private final JavaScriptExecutorFactory mJsExecutorFactory;
|
|
private final JSBundleLoader mJsBundleLoader;
|
|
|
|
public ReactContextInitParams(
|
|
JavaScriptExecutorFactory jsExecutorFactory,
|
|
JSBundleLoader jsBundleLoader) {
|
|
mJsExecutorFactory = Assertions.assertNotNull(jsExecutorFactory);
|
|
mJsBundleLoader = Assertions.assertNotNull(jsBundleLoader);
|
|
}
|
|
|
|
public JavaScriptExecutorFactory getJsExecutorFactory() {
|
|
return mJsExecutorFactory;
|
|
}
|
|
|
|
public JSBundleLoader getJsBundleLoader() {
|
|
return mJsBundleLoader;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a builder that is capable of creating an instance of {@link ReactInstanceManager}.
|
|
*/
|
|
public static ReactInstanceManagerBuilder builder() {
|
|
return new ReactInstanceManagerBuilder();
|
|
}
|
|
|
|
/* package */ ReactInstanceManager(
|
|
Context applicationContext,
|
|
@Nullable Activity currentActivity,
|
|
@Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler,
|
|
JavaScriptExecutorFactory javaScriptExecutorFactory,
|
|
@Nullable JSBundleLoader bundleLoader,
|
|
@Nullable String jsMainModulePath,
|
|
List<ReactPackage> packages,
|
|
boolean useDeveloperSupport,
|
|
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
|
|
LifecycleState initialLifecycleState,
|
|
@Nullable UIImplementationProvider mUIImplementationProvider,
|
|
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler,
|
|
@Nullable RedBoxHandler redBoxHandler,
|
|
boolean lazyViewManagersEnabled,
|
|
@Nullable DevBundleDownloadListener devBundleDownloadListener,
|
|
int minNumShakes,
|
|
int minTimeLeftInFrameForNonBatchedOperationMs,
|
|
@Nullable JSIModulePackage jsiModulePackage,
|
|
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.ctor()");
|
|
initializeSoLoaderIfNecessary(applicationContext);
|
|
|
|
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext);
|
|
|
|
mApplicationContext = applicationContext;
|
|
mCurrentActivity = currentActivity;
|
|
mDefaultBackButtonImpl = defaultHardwareBackBtnHandler;
|
|
mJavaScriptExecutorFactory = javaScriptExecutorFactory;
|
|
mBundleLoader = bundleLoader;
|
|
mJSMainModulePath = jsMainModulePath;
|
|
mPackages = new ArrayList<>();
|
|
mUseDeveloperSupport = useDeveloperSupport;
|
|
Systrace.beginSection(
|
|
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.initDevSupportManager");
|
|
mDevSupportManager =
|
|
DevSupportManagerFactory.create(
|
|
applicationContext,
|
|
createDevHelperInterface(),
|
|
mJSMainModulePath,
|
|
useDeveloperSupport,
|
|
redBoxHandler,
|
|
devBundleDownloadListener,
|
|
minNumShakes,
|
|
customPackagerCommandHandlers);
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
mBridgeIdleDebugListener = bridgeIdleDebugListener;
|
|
mLifecycleState = initialLifecycleState;
|
|
mMemoryPressureRouter = new MemoryPressureRouter(applicationContext);
|
|
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
|
|
synchronized (mPackages) {
|
|
PrinterHolder.getPrinter()
|
|
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Use Split Packages");
|
|
mPackages.add(
|
|
new CoreModulesPackage(
|
|
this,
|
|
new DefaultHardwareBackBtnHandler() {
|
|
@Override
|
|
public void invokeDefaultOnBackPressed() {
|
|
ReactInstanceManager.this.invokeDefaultOnBackPressed();
|
|
}
|
|
},
|
|
mUIImplementationProvider,
|
|
lazyViewManagersEnabled,
|
|
minTimeLeftInFrameForNonBatchedOperationMs));
|
|
if (mUseDeveloperSupport) {
|
|
mPackages.add(new DebugCorePackage());
|
|
}
|
|
mPackages.addAll(packages);
|
|
}
|
|
mJSIModulePackage = jsiModulePackage;
|
|
|
|
// Instantiate ReactChoreographer in UI thread.
|
|
ReactChoreographer.initialize();
|
|
if (mUseDeveloperSupport) {
|
|
mDevSupportManager.startInspector();
|
|
}
|
|
}
|
|
|
|
private ReactInstanceManagerDevHelper createDevHelperInterface() {
|
|
return new ReactInstanceManagerDevHelper() {
|
|
@Override
|
|
public void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
|
|
ReactInstanceManager.this.onReloadWithJSDebugger(jsExecutorFactory);
|
|
}
|
|
|
|
@Override
|
|
public void onJSBundleLoadedFromServer(@Nullable NativeDeltaClient nativeDeltaClient) {
|
|
ReactInstanceManager.this.onJSBundleLoadedFromServer(nativeDeltaClient);
|
|
}
|
|
|
|
@Override
|
|
public void toggleElementInspector() {
|
|
ReactInstanceManager.this.toggleElementInspector();
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Activity getCurrentActivity() {
|
|
return ReactInstanceManager.this.mCurrentActivity;
|
|
}
|
|
};
|
|
}
|
|
|
|
public DevSupportManager getDevSupportManager() {
|
|
return mDevSupportManager;
|
|
}
|
|
|
|
public MemoryPressureRouter getMemoryPressureRouter() {
|
|
return mMemoryPressureRouter;
|
|
}
|
|
|
|
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
|
|
// This way we don't need to require others to have additional initialization code and to
|
|
// subclass android.app.Application.
|
|
|
|
// Method SoLoader.init is idempotent, so if you wish to use native exopackage, just call
|
|
// SoLoader.init with appropriate args before initializing ReactInstanceManager
|
|
SoLoader.init(applicationContext, /* native exopackage */ false);
|
|
}
|
|
|
|
/**
|
|
* Trigger react context initialization asynchronously in a background async task. This enables
|
|
* applications to pre-load the application JS, and execute global code before
|
|
* {@link ReactRootView} is available and measured. This should only be called the first time the
|
|
* application is set up, which is enforced to keep developers from accidentally creating their
|
|
* application multiple times without realizing it.
|
|
*
|
|
* Called from UI thread.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void createReactContextInBackground() {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContextInBackground()");
|
|
Assertions.assertCondition(
|
|
!mHasStartedCreatingInitialContext,
|
|
"createReactContextInBackground should only be called when creating the react " +
|
|
"application for the first time. When reloading JS, e.g. from a new file, explicitly" +
|
|
"use recreateReactContextInBackground");
|
|
|
|
mHasStartedCreatingInitialContext = true;
|
|
recreateReactContextInBackgroundInner();
|
|
}
|
|
|
|
/**
|
|
* Recreate the react application and context. This should be called if configuration has changed
|
|
* or the developer has requested the app to be reloaded. It should only be called after an
|
|
* initial call to createReactContextInBackground.
|
|
*
|
|
* <p>Called from UI thread.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void recreateReactContextInBackground() {
|
|
Assertions.assertCondition(
|
|
mHasStartedCreatingInitialContext,
|
|
"recreateReactContextInBackground should only be called after the initial " +
|
|
"createReactContextInBackground call.");
|
|
recreateReactContextInBackgroundInner();
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
private void recreateReactContextInBackgroundInner() {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()");
|
|
PrinterHolder.getPrinter()
|
|
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground");
|
|
UiThreadUtil.assertOnUiThread();
|
|
|
|
if (mUseDeveloperSupport && mJSMainModulePath != null) {
|
|
final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
|
|
|
|
// If remote JS debugging is enabled, load from dev server.
|
|
if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
|
|
!devSettings.isRemoteJSDebugEnabled()) {
|
|
// If there is a up-to-date bundle downloaded from server,
|
|
// with remote JS debugging disabled, always use that.
|
|
onJSBundleLoadedFromServer(null);
|
|
return;
|
|
}
|
|
|
|
if (!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
|
|
if (mBundleLoader == null) {
|
|
mDevSupportManager.handleReloadJS();
|
|
} else {
|
|
mDevSupportManager.isPackagerRunning(
|
|
new PackagerStatusCallback() {
|
|
@Override
|
|
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
|
|
UiThreadUtil.runOnUiThread(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (packagerIsRunning) {
|
|
mDevSupportManager.handleReloadJS();
|
|
} else {
|
|
// If dev server is down, disable the remote JS debugging.
|
|
devSettings.setRemoteJSDebugEnabled(false);
|
|
recreateReactContextInBackgroundFromBundleLoader();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
recreateReactContextInBackgroundFromBundleLoader();
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
private void recreateReactContextInBackgroundFromBundleLoader() {
|
|
Log.d(
|
|
ReactConstants.TAG,
|
|
"ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()");
|
|
PrinterHolder.getPrinter()
|
|
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from BundleLoader");
|
|
recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader);
|
|
}
|
|
|
|
/**
|
|
* @return whether createReactContextInBackground has been called. Will return false after
|
|
* onDestroy until a new initial context has been created.
|
|
*/
|
|
public boolean hasStartedCreatingInitialContext() {
|
|
return mHasStartedCreatingInitialContext;
|
|
}
|
|
|
|
/**
|
|
* This method will give JS the opportunity to consume the back button event. If JS does not
|
|
* consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS.
|
|
*/
|
|
public void onBackPressed() {
|
|
UiThreadUtil.assertOnUiThread();
|
|
ReactContext reactContext = mCurrentReactContext;
|
|
if (reactContext == null) {
|
|
// Invoke without round trip to JS.
|
|
FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
|
|
invokeDefaultOnBackPressed();
|
|
} else {
|
|
DeviceEventManagerModule deviceEventManagerModule =
|
|
reactContext.getNativeModule(DeviceEventManagerModule.class);
|
|
deviceEventManagerModule.emitHardwareBackPressed();
|
|
}
|
|
}
|
|
|
|
private void invokeDefaultOnBackPressed() {
|
|
UiThreadUtil.assertOnUiThread();
|
|
if (mDefaultBackButtonImpl != null) {
|
|
mDefaultBackButtonImpl.invokeDefaultOnBackPressed();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method will give JS the opportunity to receive intents via Linking.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onNewIntent(Intent intent) {
|
|
UiThreadUtil.assertOnUiThread();
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (currentContext == null) {
|
|
FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
|
|
} else {
|
|
String action = intent.getAction();
|
|
Uri uri = intent.getData();
|
|
|
|
if (Intent.ACTION_VIEW.equals(action) && uri != null) {
|
|
DeviceEventManagerModule deviceEventManagerModule =
|
|
currentContext.getNativeModule(DeviceEventManagerModule.class);
|
|
deviceEventManagerModule.emitNewIntentReceived(uri);
|
|
}
|
|
currentContext.onNewIntent(mCurrentActivity, intent);
|
|
}
|
|
}
|
|
|
|
private void toggleElementInspector() {
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (currentContext != null) {
|
|
currentContext
|
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
.emit("toggleElementInspector", null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Call this from {@link Activity#onPause()}. This notifies any listening modules so they can do
|
|
* any necessary cleanup.
|
|
*
|
|
* @deprecated Use {@link #onHostPause(Activity)} instead.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onHostPause() {
|
|
UiThreadUtil.assertOnUiThread();
|
|
|
|
mDefaultBackButtonImpl = null;
|
|
if (mUseDeveloperSupport) {
|
|
mDevSupportManager.setDevSupportEnabled(false);
|
|
}
|
|
|
|
moveToBeforeResumeLifecycleState();
|
|
}
|
|
|
|
/**
|
|
* Call this from {@link Activity#onPause()}. This notifies any listening modules so they can do
|
|
* any necessary cleanup. The passed Activity is the current Activity being paused. This will
|
|
* always be the foreground activity that would be returned by
|
|
* {@link ReactContext#getCurrentActivity()}.
|
|
*
|
|
* @param activity the activity being paused
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onHostPause(Activity activity) {
|
|
Assertions.assertNotNull(mCurrentActivity);
|
|
Assertions.assertCondition(
|
|
activity == mCurrentActivity,
|
|
"Pausing an activity that is not the current activity, this is incorrect! " +
|
|
"Current activity: " + mCurrentActivity.getClass().getSimpleName() + " " +
|
|
"Paused activity: " + activity.getClass().getSimpleName());
|
|
onHostPause();
|
|
}
|
|
|
|
/**
|
|
* Use this method when the activity resumes to enable invoking the back button directly from JS.
|
|
*
|
|
* This method retains an instance to provided mDefaultBackButtonImpl. Thus it's important to pass
|
|
* from the activity instance that owns this particular instance of {@link
|
|
* ReactInstanceManager}, so that once this instance receive {@link #onHostDestroy} event it
|
|
* will clear the reference to that defaultBackButtonImpl.
|
|
*
|
|
* @param defaultBackButtonImpl a {@link DefaultHardwareBackBtnHandler} from an Activity that owns
|
|
* this instance of {@link ReactInstanceManager}.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) {
|
|
UiThreadUtil.assertOnUiThread();
|
|
|
|
mDefaultBackButtonImpl = defaultBackButtonImpl;
|
|
onHostResume(activity);
|
|
}
|
|
|
|
/**
|
|
* Use this method when the activity resumes.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onHostResume(Activity activity) {
|
|
UiThreadUtil.assertOnUiThread();
|
|
|
|
mCurrentActivity = activity;
|
|
|
|
if (mUseDeveloperSupport) {
|
|
// Resume can be called from one of two different states:
|
|
// a) when activity was paused
|
|
// b) when activity has just been created
|
|
// In case of (a) the activity is attached to window and it is ok to add new views to it or
|
|
// open dialogs. In case of (b) there is often a slight delay before such a thing happens.
|
|
// As dev support manager can add views or open dialogs immediately after it gets enabled
|
|
// (e.g. in the case when JS bundle is being fetched in background) we only want to enable
|
|
// it once we know for sure the current activity is attached.
|
|
|
|
// We check if activity is attached to window by checking if decor view is attached
|
|
final View decorView = mCurrentActivity.getWindow().getDecorView();
|
|
if (!ViewCompat.isAttachedToWindow(decorView)) {
|
|
decorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
|
@Override
|
|
public void onViewAttachedToWindow(View v) {
|
|
// we can drop listener now that we know the view is attached
|
|
decorView.removeOnAttachStateChangeListener(this);
|
|
mDevSupportManager.setDevSupportEnabled(true);
|
|
}
|
|
|
|
@Override
|
|
public void onViewDetachedFromWindow(View v) {
|
|
// do nothing
|
|
}
|
|
});
|
|
} else {
|
|
// activity is attached to window, we can enable dev support immediately
|
|
mDevSupportManager.setDevSupportEnabled(true);
|
|
}
|
|
}
|
|
|
|
moveToResumedLifecycleState(false);
|
|
}
|
|
|
|
/**
|
|
* Call this from {@link Activity#onDestroy()}. This notifies any listening modules so they can do
|
|
* any necessary cleanup.
|
|
*
|
|
* @deprecated use {@link #onHostDestroy(Activity)} instead
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onHostDestroy() {
|
|
UiThreadUtil.assertOnUiThread();
|
|
|
|
if (mUseDeveloperSupport) {
|
|
mDevSupportManager.setDevSupportEnabled(false);
|
|
}
|
|
|
|
moveToBeforeCreateLifecycleState();
|
|
mCurrentActivity = null;
|
|
}
|
|
|
|
/**
|
|
* Call this from {@link Activity#onDestroy()}. This notifies any listening modules so they can do
|
|
* any necessary cleanup. If the activity being destroyed is not the current activity, no modules
|
|
* are notified.
|
|
*
|
|
* @param activity the activity being destroyed
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void onHostDestroy(Activity activity) {
|
|
if (activity == mCurrentActivity) {
|
|
onHostDestroy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Destroy this React instance and the attached JS context.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void destroy() {
|
|
UiThreadUtil.assertOnUiThread();
|
|
PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Destroy");
|
|
|
|
mHasStartedDestroying = true;
|
|
|
|
if (mUseDeveloperSupport) {
|
|
mDevSupportManager.setDevSupportEnabled(false);
|
|
mDevSupportManager.stopInspector();
|
|
}
|
|
|
|
moveToBeforeCreateLifecycleState();
|
|
|
|
if (mCreateReactContextThread != null) {
|
|
mCreateReactContextThread = null;
|
|
}
|
|
|
|
mMemoryPressureRouter.destroy(mApplicationContext);
|
|
|
|
synchronized (mReactContextLock) {
|
|
if (mCurrentReactContext != null) {
|
|
mCurrentReactContext.destroy();
|
|
mCurrentReactContext = null;
|
|
}
|
|
}
|
|
mHasStartedCreatingInitialContext = false;
|
|
mCurrentActivity = null;
|
|
|
|
ResourceDrawableIdHelper.getInstance().clear();
|
|
mHasStartedDestroying = false;
|
|
synchronized (mHasStartedDestroying) {
|
|
mHasStartedDestroying.notifyAll();
|
|
}
|
|
}
|
|
|
|
private synchronized void moveToResumedLifecycleState(boolean force) {
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (currentContext != null) {
|
|
// we currently don't have an onCreate callback so we call onResume for both transitions
|
|
if (force ||
|
|
mLifecycleState == LifecycleState.BEFORE_RESUME ||
|
|
mLifecycleState == LifecycleState.BEFORE_CREATE) {
|
|
currentContext.onHostResume(mCurrentActivity);
|
|
}
|
|
}
|
|
mLifecycleState = LifecycleState.RESUMED;
|
|
}
|
|
|
|
private synchronized void moveToBeforeResumeLifecycleState() {
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (currentContext != null) {
|
|
if (mLifecycleState == LifecycleState.BEFORE_CREATE) {
|
|
currentContext.onHostResume(mCurrentActivity);
|
|
currentContext.onHostPause();
|
|
} else if (mLifecycleState == LifecycleState.RESUMED) {
|
|
currentContext.onHostPause();
|
|
}
|
|
}
|
|
mLifecycleState = LifecycleState.BEFORE_RESUME;
|
|
}
|
|
|
|
private synchronized void moveToBeforeCreateLifecycleState() {
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (currentContext != null) {
|
|
if (mLifecycleState == LifecycleState.RESUMED) {
|
|
currentContext.onHostPause();
|
|
mLifecycleState = LifecycleState.BEFORE_RESUME;
|
|
}
|
|
if (mLifecycleState == LifecycleState.BEFORE_RESUME) {
|
|
currentContext.onHostDestroy();
|
|
}
|
|
}
|
|
mLifecycleState = LifecycleState.BEFORE_CREATE;
|
|
}
|
|
|
|
private synchronized void moveReactContextToCurrentLifecycleState() {
|
|
if (mLifecycleState == LifecycleState.RESUMED) {
|
|
moveToResumedLifecycleState(true);
|
|
}
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (currentContext != null) {
|
|
currentContext.onActivityResult(activity, requestCode, resultCode, data);
|
|
}
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
public void showDevOptionsDialog() {
|
|
UiThreadUtil.assertOnUiThread();
|
|
mDevSupportManager.showDevOptionsDialog();
|
|
}
|
|
|
|
/**
|
|
* Attach given {@param rootView} to a catalyst instance manager and start JS application using
|
|
* JS module provided by {@link ReactRootView#getJSModuleName}. If the react context is currently
|
|
* being (re)-created, or if react context has not been created yet, the JS application associated
|
|
* with the provided root view will be started asynchronously, i.e this method won't block.
|
|
* This view will then be tracked by this manager and in case of catalyst instance restart it will
|
|
* be re-attached.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void attachRootView(ReactRootView rootView) {
|
|
UiThreadUtil.assertOnUiThread();
|
|
mAttachedRootViews.add(rootView);
|
|
|
|
// Reset view content as it's going to be populated by the application content from JS.
|
|
rootView.removeAllViews();
|
|
rootView.setId(View.NO_ID);
|
|
|
|
// If react context is being created in the background, JS application will be started
|
|
// automatically when creation completes, as root view is part of the attached root view list.
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
if (mCreateReactContextThread == null && currentContext != null) {
|
|
attachRootViewToInstance(rootView);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Detach given {@param rootView} from current catalyst instance. It's safe to call this method
|
|
* multiple times on the same {@param rootView} - in that case view will be detached with the
|
|
* first call.
|
|
*/
|
|
@ThreadConfined(UI)
|
|
public void detachRootView(ReactRootView rootView) {
|
|
UiThreadUtil.assertOnUiThread();
|
|
synchronized (mAttachedRootViews) {
|
|
if (mAttachedRootViews.contains(rootView)) {
|
|
ReactContext currentContext = getCurrentReactContext();
|
|
mAttachedRootViews.remove(rootView);
|
|
if (currentContext != null && currentContext.hasActiveCatalystInstance()) {
|
|
detachViewFromInstance(rootView, currentContext.getCatalystInstance());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uses configured {@link ReactPackage} instances to create all view managers.
|
|
*/
|
|
public List<ViewManager> getOrCreateViewManagers(
|
|
ReactApplicationContext catalystApplicationContext) {
|
|
ReactMarker.logMarker(CREATE_VIEW_MANAGERS_START);
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createAllViewManagers");
|
|
try {
|
|
if (mViewManagers == null) {
|
|
synchronized (mPackages) {
|
|
if (mViewManagers == null) {
|
|
mViewManagers = new ArrayList<>();
|
|
for (ReactPackage reactPackage : mPackages) {
|
|
mViewManagers.addAll(reactPackage.createViewManagers(catalystApplicationContext));
|
|
}
|
|
return mViewManagers;
|
|
}
|
|
}
|
|
}
|
|
return mViewManagers;
|
|
} finally {
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
ReactMarker.logMarker(CREATE_VIEW_MANAGERS_END);
|
|
}
|
|
}
|
|
|
|
public @Nullable ViewManager createViewManager(String viewManagerName) {
|
|
ReactApplicationContext context;
|
|
synchronized (mReactContextLock) {
|
|
context = (ReactApplicationContext) getCurrentReactContext();
|
|
if (context == null || !context.hasActiveCatalystInstance()) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
synchronized (mPackages) {
|
|
for (ReactPackage reactPackage : mPackages) {
|
|
if (reactPackage instanceof ViewManagerOnDemandReactPackage) {
|
|
ViewManager viewManager =
|
|
((ViewManagerOnDemandReactPackage) reactPackage)
|
|
.createViewManager(context, viewManagerName);
|
|
if (viewManager != null) {
|
|
return viewManager;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public @Nullable List<String> getViewManagerNames() {
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.getViewManagerNames");
|
|
ReactApplicationContext context;
|
|
synchronized(mReactContextLock) {
|
|
context = (ReactApplicationContext) getCurrentReactContext();
|
|
if (context == null || !context.hasActiveCatalystInstance()) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
synchronized (mPackages) {
|
|
Set<String> uniqueNames = new HashSet<>();
|
|
for (ReactPackage reactPackage : mPackages) {
|
|
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactInstanceManager.getViewManagerName")
|
|
.arg("Package", reactPackage.getClass().getSimpleName())
|
|
.flush();
|
|
if (reactPackage instanceof ViewManagerOnDemandReactPackage) {
|
|
List<String> names =
|
|
((ViewManagerOnDemandReactPackage) reactPackage).getViewManagerNames(context);
|
|
if (names != null) {
|
|
uniqueNames.addAll(names);
|
|
}
|
|
}
|
|
SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
|
|
}
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
return new ArrayList<>(uniqueNames);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a listener to be notified of react instance events.
|
|
*/
|
|
public void addReactInstanceEventListener(ReactInstanceEventListener listener) {
|
|
mReactInstanceEventListeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* Remove a listener previously added with {@link #addReactInstanceEventListener}.
|
|
*/
|
|
public void removeReactInstanceEventListener(ReactInstanceEventListener listener) {
|
|
mReactInstanceEventListeners.remove(listener);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public @Nullable ReactContext getCurrentReactContext() {
|
|
synchronized (mReactContextLock) {
|
|
return mCurrentReactContext;
|
|
}
|
|
}
|
|
|
|
public LifecycleState getLifecycleState() {
|
|
return mLifecycleState;
|
|
}
|
|
|
|
public String getJsExecutorName() {
|
|
return mJavaScriptExecutorFactory.toString();
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
private void onReloadWithJSDebugger(JavaJSExecutor.Factory jsExecutorFactory) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.onReloadWithJSDebugger()");
|
|
recreateReactContextInBackground(
|
|
new ProxyJavaScriptExecutor.Factory(jsExecutorFactory),
|
|
JSBundleLoader.createRemoteDebuggerBundleLoader(
|
|
mDevSupportManager.getJSBundleURLForRemoteDebugging(),
|
|
mDevSupportManager.getSourceUrl()));
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
private void onJSBundleLoadedFromServer(@Nullable NativeDeltaClient nativeDeltaClient) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.onJSBundleLoadedFromServer()");
|
|
|
|
JSBundleLoader bundleLoader = nativeDeltaClient == null
|
|
? JSBundleLoader.createCachedBundleFromNetworkLoader(
|
|
mDevSupportManager.getSourceUrl(),
|
|
mDevSupportManager.getDownloadedJSBundleFile())
|
|
: JSBundleLoader.createDeltaFromNetworkLoader(
|
|
mDevSupportManager.getSourceUrl(), nativeDeltaClient);
|
|
|
|
recreateReactContextInBackground(mJavaScriptExecutorFactory, bundleLoader);
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
private void recreateReactContextInBackground(
|
|
JavaScriptExecutorFactory jsExecutorFactory,
|
|
JSBundleLoader jsBundleLoader) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackground()");
|
|
UiThreadUtil.assertOnUiThread();
|
|
|
|
final ReactContextInitParams initParams = new ReactContextInitParams(
|
|
jsExecutorFactory,
|
|
jsBundleLoader);
|
|
if (mCreateReactContextThread == null) {
|
|
runCreateReactContextOnNewThread(initParams);
|
|
} else {
|
|
mPendingReactContextInitParams = initParams;
|
|
}
|
|
}
|
|
|
|
@ThreadConfined(UI)
|
|
private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
|
|
UiThreadUtil.assertOnUiThread();
|
|
synchronized (mAttachedRootViews) {
|
|
synchronized (mReactContextLock) {
|
|
if (mCurrentReactContext != null) {
|
|
tearDownReactContext(mCurrentReactContext);
|
|
mCurrentReactContext = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
mCreateReactContextThread =
|
|
new Thread(
|
|
null,
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
ReactMarker.logMarker(REACT_CONTEXT_THREAD_END);
|
|
synchronized (ReactInstanceManager.this.mHasStartedDestroying) {
|
|
while (ReactInstanceManager.this.mHasStartedDestroying) {
|
|
try {
|
|
ReactInstanceManager.this.mHasStartedDestroying.wait();
|
|
} catch (InterruptedException e) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
// As destroy() may have run and set this to false, ensure that it is true before we create
|
|
mHasStartedCreatingInitialContext = true;
|
|
|
|
try {
|
|
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
|
|
ReactMarker.logMarker(VM_INIT);
|
|
final ReactApplicationContext reactApplicationContext =
|
|
createReactContext(
|
|
initParams.getJsExecutorFactory().create(),
|
|
initParams.getJsBundleLoader());
|
|
|
|
mCreateReactContextThread = null;
|
|
ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_START);
|
|
final Runnable maybeRecreateReactContextRunnable =
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (mPendingReactContextInitParams != null) {
|
|
runCreateReactContextOnNewThread(mPendingReactContextInitParams);
|
|
mPendingReactContextInitParams = null;
|
|
}
|
|
}
|
|
};
|
|
Runnable setupReactContextRunnable =
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
setupReactContext(reactApplicationContext);
|
|
} catch (Exception e) {
|
|
mDevSupportManager.handleException(e);
|
|
}
|
|
}
|
|
};
|
|
|
|
reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable);
|
|
UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable);
|
|
} catch (Exception e) {
|
|
mDevSupportManager.handleException(e);
|
|
}
|
|
}
|
|
},
|
|
"create_react_context");
|
|
ReactMarker.logMarker(REACT_CONTEXT_THREAD_START);
|
|
mCreateReactContextThread.start();
|
|
}
|
|
|
|
private void setupReactContext(final ReactApplicationContext reactContext) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.setupReactContext()");
|
|
ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END);
|
|
ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext");
|
|
synchronized (mAttachedRootViews) {
|
|
synchronized (mReactContextLock) {
|
|
mCurrentReactContext = Assertions.assertNotNull(reactContext);
|
|
}
|
|
|
|
CatalystInstance catalystInstance =
|
|
Assertions.assertNotNull(reactContext.getCatalystInstance());
|
|
|
|
catalystInstance.initialize();
|
|
mDevSupportManager.onNewReactContextCreated(reactContext);
|
|
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
|
|
moveReactContextToCurrentLifecycleState();
|
|
|
|
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
|
|
for (ReactRootView rootView : mAttachedRootViews) {
|
|
attachRootViewToInstance(rootView);
|
|
}
|
|
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END);
|
|
}
|
|
|
|
ReactInstanceEventListener[] listeners =
|
|
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
|
|
final ReactInstanceEventListener[] finalListeners =
|
|
mReactInstanceEventListeners.toArray(listeners);
|
|
|
|
UiThreadUtil.runOnUiThread(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
for (ReactInstanceEventListener listener : finalListeners) {
|
|
listener.onReactContextInitialized(reactContext);
|
|
}
|
|
}
|
|
});
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
ReactMarker.logMarker(SETUP_REACT_CONTEXT_END);
|
|
reactContext.runOnJSQueueThread(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
|
|
ReactMarker.logMarker(CHANGE_THREAD_PRIORITY, "js_default");
|
|
}
|
|
});
|
|
reactContext.runOnNativeModulesQueueThread(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void attachRootViewToInstance(final ReactRootView rootView) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.attachRootViewToInstance()");
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance");
|
|
UIManager uiManagerModule = UIManagerHelper.getUIManager(mCurrentReactContext, rootView.getUIManagerType());
|
|
|
|
@Nullable Bundle initialProperties = rootView.getAppProperties();
|
|
final int rootTag = uiManagerModule.addRootView(
|
|
rootView,
|
|
initialProperties == null ?
|
|
new WritableNativeMap() : Arguments.fromBundle(initialProperties),
|
|
rootView.getInitialUITemplate());
|
|
rootView.setRootViewTag(rootTag);
|
|
rootView.runApplication();
|
|
Systrace.beginAsyncSection(
|
|
TRACE_TAG_REACT_JAVA_BRIDGE,
|
|
"pre_rootView.onAttachedToReactInstance",
|
|
rootTag);
|
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Systrace.endAsyncSection(
|
|
TRACE_TAG_REACT_JAVA_BRIDGE,
|
|
"pre_rootView.onAttachedToReactInstance",
|
|
rootTag);
|
|
rootView.onAttachedToReactInstance();
|
|
}
|
|
});
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
}
|
|
|
|
private void detachViewFromInstance(
|
|
ReactRootView rootView,
|
|
CatalystInstance catalystInstance) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.detachViewFromInstance()");
|
|
UiThreadUtil.assertOnUiThread();
|
|
if (rootView.getUIManagerType() == FABRIC) {
|
|
catalystInstance.getJSModule(ReactFabric.class)
|
|
.unmountComponentAtNode(rootView.getRootViewTag());
|
|
} else {
|
|
catalystInstance.getJSModule(AppRegistry.class)
|
|
.unmountApplicationComponentAtRootTag(rootView.getRootViewTag());
|
|
}
|
|
|
|
}
|
|
|
|
private void tearDownReactContext(ReactContext reactContext) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.tearDownReactContext()");
|
|
UiThreadUtil.assertOnUiThread();
|
|
if (mLifecycleState == LifecycleState.RESUMED) {
|
|
reactContext.onHostPause();
|
|
}
|
|
|
|
synchronized (mAttachedRootViews) {
|
|
for (ReactRootView rootView : mAttachedRootViews) {
|
|
rootView.removeAllViews();
|
|
rootView.setId(View.NO_ID);
|
|
}
|
|
}
|
|
|
|
reactContext.destroy();
|
|
mDevSupportManager.onReactInstanceDestroyed(reactContext);
|
|
mMemoryPressureRouter.removeMemoryPressureListener(reactContext.getCatalystInstance());
|
|
}
|
|
|
|
/**
|
|
* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
|
|
*/
|
|
private ReactApplicationContext createReactContext(
|
|
JavaScriptExecutor jsExecutor,
|
|
JSBundleLoader jsBundleLoader) {
|
|
Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");
|
|
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START, jsExecutor.getName());
|
|
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
|
|
|
|
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
|
|
? mNativeModuleCallExceptionHandler
|
|
: mDevSupportManager;
|
|
reactContext.setNativeModuleCallExceptionHandler(exceptionHandler);
|
|
|
|
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
|
|
|
|
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
|
|
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
|
|
.setJSExecutor(jsExecutor)
|
|
.setRegistry(nativeModuleRegistry)
|
|
.setJSBundleLoader(jsBundleLoader)
|
|
.setNativeModuleCallExceptionHandler(exceptionHandler);
|
|
|
|
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
|
|
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
|
|
final CatalystInstance catalystInstance;
|
|
try {
|
|
catalystInstance = catalystInstanceBuilder.build();
|
|
} finally {
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
|
|
}
|
|
if (mJSIModulePackage != null) {
|
|
catalystInstance.addJSIModules(mJSIModulePackage
|
|
.getJSIModules(reactContext, catalystInstance.getJavaScriptContextHolder()));
|
|
}
|
|
|
|
if (mBridgeIdleDebugListener != null) {
|
|
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
|
|
}
|
|
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
|
|
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
|
|
}
|
|
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
|
|
catalystInstance.runJSBundle();
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
|
|
reactContext.initializeWithInstance(catalystInstance);
|
|
|
|
|
|
return reactContext;
|
|
}
|
|
|
|
private NativeModuleRegistry processPackages(
|
|
ReactApplicationContext reactContext,
|
|
List<ReactPackage> packages,
|
|
boolean checkAndUpdatePackageMembership) {
|
|
NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(
|
|
reactContext,
|
|
this);
|
|
|
|
ReactMarker.logMarker(PROCESS_PACKAGES_START);
|
|
|
|
// TODO(6818138): Solve use-case of native modules overriding
|
|
synchronized (mPackages) {
|
|
for (ReactPackage reactPackage : packages) {
|
|
if (checkAndUpdatePackageMembership && mPackages.contains(reactPackage)) {
|
|
continue;
|
|
}
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createAndProcessCustomReactPackage");
|
|
try {
|
|
if (checkAndUpdatePackageMembership) {
|
|
mPackages.add(reactPackage);
|
|
}
|
|
processPackage(reactPackage, nativeModuleRegistryBuilder);
|
|
} finally {
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
}
|
|
}
|
|
}
|
|
ReactMarker.logMarker(PROCESS_PACKAGES_END);
|
|
|
|
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
|
|
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
|
|
NativeModuleRegistry nativeModuleRegistry;
|
|
try {
|
|
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
|
|
} finally {
|
|
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
|
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
|
|
}
|
|
|
|
return nativeModuleRegistry;
|
|
}
|
|
|
|
private void processPackage(
|
|
ReactPackage reactPackage,
|
|
NativeModuleRegistryBuilder nativeModuleRegistryBuilder) {
|
|
SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "processPackage")
|
|
.arg("className", reactPackage.getClass().getSimpleName())
|
|
.flush();
|
|
if (reactPackage instanceof ReactPackageLogger) {
|
|
((ReactPackageLogger) reactPackage).startProcessPackage();
|
|
}
|
|
nativeModuleRegistryBuilder.processPackage(reactPackage);
|
|
|
|
if (reactPackage instanceof ReactPackageLogger) {
|
|
((ReactPackageLogger) reactPackage).endProcessPackage();
|
|
}
|
|
SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
|
|
}
|
|
}
|