diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index ef3782322..d7cf0c8a7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -137,13 +137,18 @@ public class ReactInstanceManager { 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 mReactInstanceEventListeners = Collections.synchronizedSet(new HashSet()); + // Identifies whether the instance manager is or soon will be initialized (on background thread) 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 MemoryPressureRouter mMemoryPressureRouter; private final @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; @@ -473,13 +478,13 @@ public class ReactInstanceManager { public void onBackPressed() { UiThreadUtil.assertOnUiThread(); ReactContext reactContext = mCurrentReactContext; - if (mCurrentReactContext == null) { + if (reactContext == null) { // Invoke without round trip to JS. FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); invokeDefaultOnBackPressed(); } else { DeviceEventManagerModule deviceEventManagerModule = - Assertions.assertNotNull(reactContext).getNativeModule(DeviceEventManagerModule.class); + reactContext.getNativeModule(DeviceEventManagerModule.class); deviceEventManagerModule.emitHardwareBackPressed(); } } @@ -497,7 +502,8 @@ public class ReactInstanceManager { @ThreadConfined(UI) public void onNewIntent(Intent intent) { UiThreadUtil.assertOnUiThread(); - if (mCurrentReactContext == null) { + ReactContext currentContext = getCurrentReactContext(); + if (currentContext == null) { FLog.w(ReactConstants.TAG, "Instance detached from instance manager"); } else { String action = intent.getAction(); @@ -505,18 +511,18 @@ public class ReactInstanceManager { if (Intent.ACTION_VIEW.equals(action) && uri != null) { DeviceEventManagerModule deviceEventManagerModule = - Assertions.assertNotNull(mCurrentReactContext) - .getNativeModule(DeviceEventManagerModule.class); + currentContext.getNativeModule(DeviceEventManagerModule.class); deviceEventManagerModule.emitNewIntentReceived(uri); } - mCurrentReactContext.onNewIntent(mCurrentActivity, intent); + currentContext.onNewIntent(mCurrentActivity, intent); } } private void toggleElementInspector() { - if (mCurrentReactContext != null) { - mCurrentReactContext + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null) { + currentContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("toggleElementInspector", null); } @@ -623,6 +629,8 @@ public class ReactInstanceManager { UiThreadUtil.assertOnUiThread(); PrinterHolder.getPrinter().logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Destroy"); + mHasStartedDestroying = true; + if (mUseDeveloperSupport) { mDevSupportManager.setDevSupportEnabled(false); mDevSupportManager.stopInspector(); @@ -631,54 +639,62 @@ public class ReactInstanceManager { moveToBeforeCreateLifecycleState(); if (mCreateReactContextThread != null) { - mCreateReactContextThread.interrupt(); mCreateReactContextThread = null; } mMemoryPressureRouter.destroy(mApplicationContext); - if (mCurrentReactContext != null) { - mCurrentReactContext.destroy(); - mCurrentReactContext = null; - mHasStartedCreatingInitialContext = false; + 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) { - if (mCurrentReactContext != null) { + 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) { - mCurrentReactContext.onHostResume(mCurrentActivity); + currentContext.onHostResume(mCurrentActivity); } } mLifecycleState = LifecycleState.RESUMED; } private synchronized void moveToBeforeResumeLifecycleState() { - if (mCurrentReactContext != null) { + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null) { if (mLifecycleState == LifecycleState.BEFORE_CREATE) { - mCurrentReactContext.onHostResume(mCurrentActivity); - mCurrentReactContext.onHostPause(); + currentContext.onHostResume(mCurrentActivity); + currentContext.onHostPause(); } else if (mLifecycleState == LifecycleState.RESUMED) { - mCurrentReactContext.onHostPause(); + currentContext.onHostPause(); } } mLifecycleState = LifecycleState.BEFORE_RESUME; } private synchronized void moveToBeforeCreateLifecycleState() { - if (mCurrentReactContext != null) { + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null) { if (mLifecycleState == LifecycleState.RESUMED) { - mCurrentReactContext.onHostPause(); + currentContext.onHostPause(); mLifecycleState = LifecycleState.BEFORE_RESUME; } if (mLifecycleState == LifecycleState.BEFORE_RESUME) { - mCurrentReactContext.onHostDestroy(); + currentContext.onHostDestroy(); } } mLifecycleState = LifecycleState.BEFORE_CREATE; @@ -692,8 +708,9 @@ public class ReactInstanceManager { @ThreadConfined(UI) public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { - if (mCurrentReactContext != null) { - mCurrentReactContext.onActivityResult(activity, requestCode, resultCode, data); + ReactContext currentContext = getCurrentReactContext(); + 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 // automatically when creation completes, as root view is part of the attached root view list. - if (mCreateReactContextThread == null && mCurrentReactContext != null) { - attachRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance()); + ReactContext currentContext = getCurrentReactContext(); + if (mCreateReactContextThread == null && currentContext != null) { + attachRootViewToInstance(rootView, currentContext.getCatalystInstance()); } } @@ -736,8 +754,9 @@ public class ReactInstanceManager { public void detachRootView(ReactRootView rootView) { UiThreadUtil.assertOnUiThread(); if (mAttachedRootViews.remove(rootView)) { - if (mCurrentReactContext != null && mCurrentReactContext.hasActiveCatalystInstance()) { - detachViewFromInstance(rootView, mCurrentReactContext.getCatalystInstance()); + ReactContext currentContext = getCurrentReactContext(); + if (currentContext != null && currentContext.hasActiveCatalystInstance()) { + detachViewFromInstance(rootView, currentContext.getCatalystInstance()); } } } @@ -777,7 +796,9 @@ public class ReactInstanceManager { @VisibleForTesting public @Nullable ReactContext getCurrentReactContext() { - return mCurrentReactContext; + synchronized (mReactContextLock) { + return mCurrentReactContext; + } } public LifecycleState getLifecycleState() { @@ -828,14 +849,28 @@ public class ReactInstanceManager { private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) { Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()"); UiThreadUtil.assertOnUiThread(); - if (mCurrentReactContext != null) { - tearDownReactContext(mCurrentReactContext); - mCurrentReactContext = null; + synchronized (mReactContextLock) { + if (mCurrentReactContext != null) { + tearDownReactContext(mCurrentReactContext); + mCurrentReactContext = null; + } } mCreateReactContextThread = new Thread(new Runnable() { @Override 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 { Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY); final ReactApplicationContext reactApplicationContext = createReactContext( @@ -879,7 +914,9 @@ public class ReactInstanceManager { ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END); ReactMarker.logMarker(SETUP_REACT_CONTEXT_START); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext"); - mCurrentReactContext = Assertions.assertNotNull(reactContext); + synchronized (mReactContextLock) { + mCurrentReactContext = Assertions.assertNotNull(reactContext); + } CatalystInstance catalystInstance = Assertions.assertNotNull(reactContext.getCatalystInstance());