Detangle destroy and create accesses

Differential Revision: D5823786

fbshipit-source-id: d6a6e29b856361a6b1d5ab48397607a844b5ab53
This commit is contained in:
Kathy Gray 2017-09-20 04:59:42 -07:00 committed by Facebook Github Bot
parent 628cbe170b
commit 6334ed2ff3
1 changed files with 70 additions and 33 deletions

View File

@ -137,13 +137,18 @@ public class ReactInstanceManager {
private final DevSupportManager mDevSupportManager; private final DevSupportManager mDevSupportManager;
private final boolean mUseDeveloperSupport; private final boolean mUseDeveloperSupport;
private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener; private final @Nullable NotThreadSafeBridgeIdleDebugListener mBridgeIdleDebugListener;
private final Object mReactContextLock = new Object();
private @Nullable volatile ReactContext mCurrentReactContext; private @Nullable volatile ReactContext mCurrentReactContext;
private final Context mApplicationContext; private final Context mApplicationContext;
private @Nullable @ThreadConfined(UI) DefaultHardwareBackBtnHandler mDefaultBackButtonImpl; private @Nullable @ThreadConfined(UI) DefaultHardwareBackBtnHandler mDefaultBackButtonImpl;
private @Nullable Activity mCurrentActivity; private @Nullable Activity mCurrentActivity;
private final Collection<ReactInstanceEventListener> mReactInstanceEventListeners = private final Collection<ReactInstanceEventListener> mReactInstanceEventListeners =
Collections.synchronizedSet(new HashSet<ReactInstanceEventListener>()); Collections.synchronizedSet(new HashSet<ReactInstanceEventListener>());
// Identifies whether the instance manager is or soon will be initialized (on background thread)
private volatile boolean mHasStartedCreatingInitialContext = false; private volatile boolean mHasStartedCreatingInitialContext = false;
// Identifies whether the insance 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 UIImplementationProvider mUIImplementationProvider; private final UIImplementationProvider mUIImplementationProvider;
private final MemoryPressureRouter mMemoryPressureRouter; private final MemoryPressureRouter mMemoryPressureRouter;
private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler;
@ -473,13 +478,13 @@ public class ReactInstanceManager {
public void onBackPressed() { public void onBackPressed() {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
ReactContext reactContext = mCurrentReactContext; ReactContext reactContext = mCurrentReactContext;
if (mCurrentReactContext == null) { if (reactContext == null) {
// Invoke without round trip to JS. // Invoke without round trip to JS.
FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
invokeDefaultOnBackPressed(); invokeDefaultOnBackPressed();
} else { } else {
DeviceEventManagerModule deviceEventManagerModule = DeviceEventManagerModule deviceEventManagerModule =
Assertions.assertNotNull(reactContext).getNativeModule(DeviceEventManagerModule.class); reactContext.getNativeModule(DeviceEventManagerModule.class);
deviceEventManagerModule.emitHardwareBackPressed(); deviceEventManagerModule.emitHardwareBackPressed();
} }
} }
@ -497,7 +502,8 @@ public class ReactInstanceManager {
@ThreadConfined(UI) @ThreadConfined(UI)
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
if (mCurrentReactContext == null) { ReactContext currentContext = getCurrentReactContext();
if (currentContext == null) {
FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); FLog.w(ReactConstants.TAG, "Instance detached from instance manager");
} else { } else {
String action = intent.getAction(); String action = intent.getAction();
@ -505,18 +511,18 @@ public class ReactInstanceManager {
if (Intent.ACTION_VIEW.equals(action) && uri != null) { if (Intent.ACTION_VIEW.equals(action) && uri != null) {
DeviceEventManagerModule deviceEventManagerModule = DeviceEventManagerModule deviceEventManagerModule =
Assertions.assertNotNull(mCurrentReactContext) currentContext.getNativeModule(DeviceEventManagerModule.class);
.getNativeModule(DeviceEventManagerModule.class);
deviceEventManagerModule.emitNewIntentReceived(uri); deviceEventManagerModule.emitNewIntentReceived(uri);
} }
mCurrentReactContext.onNewIntent(mCurrentActivity, intent); currentContext.onNewIntent(mCurrentActivity, intent);
} }
} }
private void toggleElementInspector() { private void toggleElementInspector() {
if (mCurrentReactContext != null) { ReactContext currentContext = getCurrentReactContext();
mCurrentReactContext if (currentContext != null) {
currentContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("toggleElementInspector", null); .emit("toggleElementInspector", null);
} }
@ -623,6 +629,8 @@ public class ReactInstanceManager {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Destroy"); PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Destroy");
mHasStartedDestroying = true;
if (mUseDeveloperSupport) { if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false); mDevSupportManager.setDevSupportEnabled(false);
mDevSupportManager.stopInspector(); mDevSupportManager.stopInspector();
@ -631,54 +639,62 @@ public class ReactInstanceManager {
moveToBeforeCreateLifecycleState(); moveToBeforeCreateLifecycleState();
if (mCreateReactContextThread != null) { if (mCreateReactContextThread != null) {
mCreateReactContextThread.interrupt();
mCreateReactContextThread = null; mCreateReactContextThread = null;
} }
mMemoryPressureRouter.destroy(mApplicationContext); mMemoryPressureRouter.destroy(mApplicationContext);
if (mCurrentReactContext != null) { synchronized (mReactContextLock) {
mCurrentReactContext.destroy(); if (mCurrentReactContext != null) {
mCurrentReactContext = null; mCurrentReactContext.destroy();
mHasStartedCreatingInitialContext = false; mCurrentReactContext = null;
}
} }
mHasStartedCreatingInitialContext = false;
mCurrentActivity = null; mCurrentActivity = null;
ResourceDrawableIdHelper.getInstance().clear(); ResourceDrawableIdHelper.getInstance().clear();
mHasStartedDestroying = false;
synchronized (mHasStartedDestroying) {
mHasStartedDestroying.notifyAll();
}
} }
private synchronized void moveToResumedLifecycleState(boolean force) { private synchronized void moveToResumedLifecycleState(boolean force) {
if (mCurrentReactContext != null) { ReactContext currentContext = getCurrentReactContext();
if (currentContext != null) {
// we currently don't have an onCreate callback so we call onResume for both transitions // we currently don't have an onCreate callback so we call onResume for both transitions
if (force || if (force ||
mLifecycleState == LifecycleState.BEFORE_RESUME || mLifecycleState == LifecycleState.BEFORE_RESUME ||
mLifecycleState == LifecycleState.BEFORE_CREATE) { mLifecycleState == LifecycleState.BEFORE_CREATE) {
mCurrentReactContext.onHostResume(mCurrentActivity); currentContext.onHostResume(mCurrentActivity);
} }
} }
mLifecycleState = LifecycleState.RESUMED; mLifecycleState = LifecycleState.RESUMED;
} }
private synchronized void moveToBeforeResumeLifecycleState() { private synchronized void moveToBeforeResumeLifecycleState() {
if (mCurrentReactContext != null) { ReactContext currentContext = getCurrentReactContext();
if (currentContext != null) {
if (mLifecycleState == LifecycleState.BEFORE_CREATE) { if (mLifecycleState == LifecycleState.BEFORE_CREATE) {
mCurrentReactContext.onHostResume(mCurrentActivity); currentContext.onHostResume(mCurrentActivity);
mCurrentReactContext.onHostPause(); currentContext.onHostPause();
} else if (mLifecycleState == LifecycleState.RESUMED) { } else if (mLifecycleState == LifecycleState.RESUMED) {
mCurrentReactContext.onHostPause(); currentContext.onHostPause();
} }
} }
mLifecycleState = LifecycleState.BEFORE_RESUME; mLifecycleState = LifecycleState.BEFORE_RESUME;
} }
private synchronized void moveToBeforeCreateLifecycleState() { private synchronized void moveToBeforeCreateLifecycleState() {
if (mCurrentReactContext != null) { ReactContext currentContext = getCurrentReactContext();
if (currentContext != null) {
if (mLifecycleState == LifecycleState.RESUMED) { if (mLifecycleState == LifecycleState.RESUMED) {
mCurrentReactContext.onHostPause(); currentContext.onHostPause();
mLifecycleState = LifecycleState.BEFORE_RESUME; mLifecycleState = LifecycleState.BEFORE_RESUME;
} }
if (mLifecycleState == LifecycleState.BEFORE_RESUME) { if (mLifecycleState == LifecycleState.BEFORE_RESUME) {
mCurrentReactContext.onHostDestroy(); currentContext.onHostDestroy();
} }
} }
mLifecycleState = LifecycleState.BEFORE_CREATE; mLifecycleState = LifecycleState.BEFORE_CREATE;
@ -692,8 +708,9 @@ public class ReactInstanceManager {
@ThreadConfined(UI) @ThreadConfined(UI)
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
if (mCurrentReactContext != null) { ReactContext currentContext = getCurrentReactContext();
mCurrentReactContext.onActivityResult(activity, requestCode, resultCode, data); if (currentContext != null) {
currentContext.onActivityResult(activity, requestCode, resultCode, data);
} }
} }
@ -722,8 +739,9 @@ public class ReactInstanceManager {
// If react context is being created in the background, JS application will be started // 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. // automatically when creation completes, as root view is part of the attached root view list.
if (mCreateReactContextThread == null && mCurrentReactContext != null) { ReactContext currentContext = getCurrentReactContext();
attachRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance()); if (mCreateReactContextThread == null && currentContext != null) {
attachRootViewToInstance(rootView, currentContext.getCatalystInstance());
} }
} }
@ -736,8 +754,9 @@ public class ReactInstanceManager {
public void detachRootView(ReactRootView rootView) { public void detachRootView(ReactRootView rootView) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
if (mAttachedRootViews.remove(rootView)) { if (mAttachedRootViews.remove(rootView)) {
if (mCurrentReactContext != null && mCurrentReactContext.hasActiveCatalystInstance()) { ReactContext currentContext = getCurrentReactContext();
detachViewFromInstance(rootView, mCurrentReactContext.getCatalystInstance()); if (currentContext != null && currentContext.hasActiveCatalystInstance()) {
detachViewFromInstance(rootView, currentContext.getCatalystInstance());
} }
} }
} }
@ -777,7 +796,9 @@ public class ReactInstanceManager {
@VisibleForTesting @VisibleForTesting
public @Nullable ReactContext getCurrentReactContext() { public @Nullable ReactContext getCurrentReactContext() {
return mCurrentReactContext; synchronized (mReactContextLock) {
return mCurrentReactContext;
}
} }
public LifecycleState getLifecycleState() { public LifecycleState getLifecycleState() {
@ -828,14 +849,28 @@ public class ReactInstanceManager {
private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) { private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()"); Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
if (mCurrentReactContext != null) { synchronized (mReactContextLock) {
tearDownReactContext(mCurrentReactContext); if (mCurrentReactContext != null) {
mCurrentReactContext = null; tearDownReactContext(mCurrentReactContext);
mCurrentReactContext = null;
}
} }
mCreateReactContextThread = new Thread(new Runnable() { mCreateReactContextThread = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
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 { try {
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY); Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
final ReactApplicationContext reactApplicationContext = createReactContext( final ReactApplicationContext reactApplicationContext = createReactContext(
@ -879,7 +914,9 @@ public class ReactInstanceManager {
ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END); ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END);
ReactMarker.logMarker(SETUP_REACT_CONTEXT_START); ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext"); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext");
mCurrentReactContext = Assertions.assertNotNull(reactContext); synchronized (mReactContextLock) {
mCurrentReactContext = Assertions.assertNotNull(reactContext);
}
CatalystInstance catalystInstance = CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance()); Assertions.assertNotNull(reactContext.getCatalystInstance());