diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java index 9297f59a2..f8cdb6f11 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactAppTestActivity.java @@ -75,7 +75,7 @@ public class ReactAppTestActivity extends FragmentActivity implements overridePendingTransition(0, 0); if (mReactInstanceManager != null) { - mReactInstanceManager.onPause(); + mReactInstanceManager.onHostPause(); } } @@ -86,7 +86,7 @@ public class ReactAppTestActivity extends FragmentActivity implements mLifecycleState = LifecycleState.RESUMED; if (mReactInstanceManager != null) { - mReactInstanceManager.onResume(this, this); + mReactInstanceManager.onHostResume(this, this); } } @@ -96,7 +96,7 @@ public class ReactAppTestActivity extends FragmentActivity implements mDestroyCountDownLatch.countDown(); if (mReactInstanceManager != null) { - mReactInstanceManager.onDestroy(); + mReactInstanceManager.destroy(); } } @@ -114,7 +114,7 @@ public class ReactAppTestActivity extends FragmentActivity implements public void resetRootViewForScreenshotTests() { if (mReactInstanceManager != null) { - mReactInstanceManager.onDestroy(); + mReactInstanceManager.destroy(); mReactInstanceManager = null; } mReactRootView = new ReactRootView(this); @@ -148,7 +148,7 @@ public class ReactAppTestActivity extends FragmentActivity implements .setInitialLifecycleState(mLifecycleState); mReactInstanceManager = builder.build(); - mReactInstanceManager.onResume(this, this); + mReactInstanceManager.onHostResume(this, this); Assertions.assertNotNull(mReactRootView).getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java index 5161a4aaa..e1312d36a 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/testing/ReactIntegrationTestCase.java @@ -74,7 +74,7 @@ public abstract class ReactIntegrationTestCase extends AndroidTestCase { @Override public void run() { if (contextToDestroy != null) { - contextToDestroy.onDestroy(); + contextToDestroy.destroy(); } } }); diff --git a/ReactAndroid/src/main/java/com/facebook/react/LifecycleState.java b/ReactAndroid/src/main/java/com/facebook/react/LifecycleState.java index f8598e908..068bd063f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/LifecycleState.java +++ b/ReactAndroid/src/main/java/com/facebook/react/LifecycleState.java @@ -21,7 +21,7 @@ package com.facebook.react; * RESUMED */ public enum LifecycleState { - + BEFORE_CREATE, BEFORE_RESUME, RESUMED, } diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java b/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java index c9679fafb..d708e7dc2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java @@ -150,7 +150,7 @@ public abstract class ReactActivity extends Activity implements DefaultHardwareB mLifecycleState = LifecycleState.BEFORE_RESUME; if (mReactInstanceManager != null) { - mReactInstanceManager.onPause(); + mReactInstanceManager.onHostPause(); } } @@ -161,7 +161,7 @@ public abstract class ReactActivity extends Activity implements DefaultHardwareB mLifecycleState = LifecycleState.RESUMED; if (mReactInstanceManager != null) { - mReactInstanceManager.onResume(this, this); + mReactInstanceManager.onHostResume(this, this); } } @@ -170,7 +170,7 @@ public abstract class ReactActivity extends Activity implements DefaultHardwareB super.onDestroy(); if (mReactInstanceManager != null) { - mReactInstanceManager.onDestroy(); + mReactInstanceManager.destroy(); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java index 1bcd68cc2..9ef96f5b8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java @@ -41,8 +41,8 @@ import com.facebook.react.uimanager.ViewManager; * 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 #onPause}, - * {@link #onDestroy} and {@link #onResume}). + * owning activity's lifecycle events to the instance manager (see {@link #onHostPause}, + * {@link #onHostDestroy} and {@link #onHostResume}). * * Ideally, this would be an interface, but because of the API used by earlier versions, it has to * have a static method, and so cannot (in Java < 8), be one. @@ -84,22 +84,32 @@ public abstract class ReactInstanceManager { * consume the event, mDefaultBackButtonImpl will be invoked at the end of the round trip to JS. */ public abstract void onBackPressed(); - public abstract void onPause(); + + /** + * Call this from {@link Activity#onPause()}. This notifies any listening modules so they can do + * any necessary cleanup. + */ + public abstract void 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 #onDestroy} event it will + * 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}. */ - public abstract void onResume( + public abstract void onHostResume( Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl); - public abstract void onDestroy(); + + /** + * Call this from {@link Activity#onDestroy()}. This notifies any listening modules so they can do + * any necessary cleanup. + */ + public abstract void onHostDestroy(); public abstract void onActivityResult(int requestCode, int resultCode, Intent data); public abstract void showDevOptionsDialog(); @@ -125,6 +135,11 @@ public abstract class ReactInstanceManager { */ public abstract void detachRootView(ReactRootView rootView); + /** + * Destroy this React instance and the attached JS context. + */ + public abstract void destroy(); + /** * Uses configured {@link ReactPackage} instances to create all view managers */ diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java index 1b8ac840a..63380ecb0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerImpl.java @@ -18,8 +18,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; import android.app.Activity; import android.app.Application; @@ -73,7 +71,18 @@ import com.facebook.react.uimanager.ViewManager; import com.facebook.soloader.SoLoader; import com.facebook.systrace.Systrace; -import static com.facebook.react.bridge.ReactMarkerConstants.*; +import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_JS_MODULE_CONFIG_END; +import static com.facebook.react.bridge.ReactMarkerConstants.BUILD_JS_MODULE_CONFIG_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_END; +import static com.facebook.react.bridge.ReactMarkerConstants.CREATE_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.RUN_JS_BUNDLE_END; +import static com.facebook.react.bridge.ReactMarkerConstants.RUN_JS_BUNDLE_START; /** * This class is managing instances of {@link CatalystInstance}. It expose a way to configure @@ -87,8 +96,8 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; * The lifecycle of the instance of {@link ReactInstanceManagerImpl} 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 #onPause}, - * {@link #onDestroy} and {@link #onResume}). + * 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}. */ @@ -458,20 +467,16 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; } @Override - public void onPause() { + public void onHostPause() { UiThreadUtil.assertOnUiThread(); - mLifecycleState = LifecycleState.BEFORE_RESUME; - mDefaultBackButtonImpl = null; if (mUseDeveloperSupport) { mDevSupportManager.setDevSupportEnabled(false); } + moveToBeforeResumeLifecycleState(); mCurrentActivity = null; - if (mCurrentReactContext != null) { - mCurrentReactContext.onPause(); - } } /** @@ -479,17 +484,16 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; * * 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 - * ReactInstanceManagerImpl}, so that once this instance receive {@link #onDestroy} event it will + * ReactInstanceManagerImpl}, 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 ReactInstanceManagerImpl}. */ @Override - public void onResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { + public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) { UiThreadUtil.assertOnUiThread(); - mLifecycleState = LifecycleState.RESUMED; mDefaultBackButtonImpl = defaultBackButtonImpl; if (mUseDeveloperSupport) { @@ -497,32 +501,82 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; } mCurrentActivity = activity; - if (mCurrentReactContext != null) { - mCurrentReactContext.onResume(activity); - } + moveToResumedLifecycleState(false); } @Override - public void onDestroy() { + public void onHostDestroy() { UiThreadUtil.assertOnUiThread(); + if (mUseDeveloperSupport) { + mDevSupportManager.setDevSupportEnabled(false); + } + + moveToBeforeCreateLifecycleState(); + mCurrentActivity = null; + } + + @Override + public void destroy() { + UiThreadUtil.assertOnUiThread(); + + if (mUseDeveloperSupport) { + mDevSupportManager.setDevSupportEnabled(false); + } + + moveToBeforeCreateLifecycleState(); + if (mReactContextInitAsyncTask != null) { mReactContextInitAsyncTask.cancel(true); } mMemoryPressureRouter.destroy(mApplicationContext); - if (mUseDeveloperSupport) { - mDevSupportManager.setDevSupportEnabled(false); - } if (mCurrentReactContext != null) { - mCurrentReactContext.onDestroy(); + mCurrentReactContext.destroy(); mCurrentReactContext = null; mHasStartedCreatingInitialContext = false; } mCurrentActivity = null; } + private void moveToResumedLifecycleState(boolean force) { + if (mCurrentReactContext != 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); + } + } + mLifecycleState = LifecycleState.RESUMED; + } + + private void moveToBeforeResumeLifecycleState() { + if (mCurrentReactContext != null) { + if (mLifecycleState == LifecycleState.BEFORE_CREATE) { + mCurrentReactContext.onHostResume(mCurrentActivity); + mCurrentReactContext.onHostPause(); + } else if (mLifecycleState == LifecycleState.RESUMED) { + mCurrentReactContext.onHostPause(); + } + } + mLifecycleState = LifecycleState.BEFORE_RESUME; + } + + private void moveToBeforeCreateLifecycleState() { + if (mCurrentReactContext != null) { + if (mLifecycleState == LifecycleState.RESUMED) { + mCurrentReactContext.onHostPause(); + mLifecycleState = LifecycleState.BEFORE_RESUME; + } + if (mLifecycleState == LifecycleState.BEFORE_RESUME) { + mCurrentReactContext.onHostDestroy(); + } + } + mLifecycleState = LifecycleState.BEFORE_CREATE; + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (mCurrentReactContext != null) { @@ -657,7 +711,7 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; catalystInstance.initialize(); mDevSupportManager.onNewReactContextCreated(reactContext); mMemoryPressureRouter.onNewReactContextCreated(reactContext); - moveReactContextToCurrentLifecycleState(reactContext); + moveReactContextToCurrentLifecycleState(); for (ReactRootView rootView : mAttachedRootViews) { attachMeasuredRootViewToInstance(rootView, catalystInstance); @@ -706,12 +760,12 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; private void tearDownReactContext(ReactContext reactContext) { UiThreadUtil.assertOnUiThread(); if (mLifecycleState == LifecycleState.RESUMED) { - reactContext.onPause(); + reactContext.onHostPause(); } for (ReactRootView rootView : mAttachedRootViews) { detachViewFromInstance(rootView, reactContext.getCatalystInstance()); } - reactContext.onDestroy(); + reactContext.destroy(); mDevSupportManager.onReactInstanceDestroyed(reactContext); mMemoryPressureRouter.onReactInstanceDestroyed(); } @@ -831,9 +885,9 @@ import static com.facebook.react.bridge.ReactMarkerConstants.*; } } - private void moveReactContextToCurrentLifecycleState(ReactApplicationContext reactContext) { + private void moveReactContextToCurrentLifecycleState() { if (mLifecycleState == LifecycleState.RESUMED) { - reactContext.onResume(mCurrentActivity); + moveToResumedLifecycleState(true); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java index 084ed5c20..4ef692a8b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java @@ -133,7 +133,7 @@ public class ReactContext extends ContextWrapper { /** * Should be called by the hosting Fragment in {@link Fragment#onResume} */ - public void onResume(@Nullable Activity activity) { + public void onHostResume(@Nullable Activity activity) { UiThreadUtil.assertOnUiThread(); mCurrentActivity = activity; for (LifecycleEventListener listener : mLifecycleEventListeners) { @@ -144,7 +144,7 @@ public class ReactContext extends ContextWrapper { /** * Should be called by the hosting Fragment in {@link Fragment#onPause} */ - public void onPause() { + public void onHostPause() { UiThreadUtil.assertOnUiThread(); for (LifecycleEventListener listener : mLifecycleEventListeners) { listener.onHostPause(); @@ -154,15 +154,23 @@ public class ReactContext extends ContextWrapper { /** * Should be called by the hosting Fragment in {@link Fragment#onDestroy} */ - public void onDestroy() { + public void onHostDestroy() { UiThreadUtil.assertOnUiThread(); for (LifecycleEventListener listener : mLifecycleEventListeners) { listener.onHostDestroy(); } + mCurrentActivity = null; + } + + /** + * Destroy this instance, making it unusable. + */ + public void destroy() { + UiThreadUtil.assertOnUiThread(); + if (mCatalystInstance != null) { mCatalystInstance.destroy(); } - mCurrentActivity = null; } /**