check lifecycle event is coming from current activity

Summary:
If a paused activity is destroyed (e.g. because of resource contention), we send onHostDestroyed to all modules even if there's an on-screen, resumed activity using the current react instance.

This diff adds a check to make sure lifecycle events come from the current activity, and ignores ones that don't.

Reviewed By: astreet

Differential Revision: D3655422

fbshipit-source-id: 0f95fda124df3732447853b9bc34c40836a4b1da
This commit is contained in:
Felix Oghina 2016-08-08 09:01:16 -07:00 committed by Facebook Github Bot 1
parent 46dc46a3b3
commit 0b5c61250b
3 changed files with 66 additions and 5 deletions

View File

@ -97,8 +97,22 @@ public abstract class ReactInstanceManager {
/**
* Call this from {@link Activity#onPause()}. This notifies any listening modules so they can do
* any necessary cleanup.
*
* @deprecated Use {@link #onHostPause(Activity)} instead.
*/
@Deprecated
public abstract void onHostPause();
/**
* 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
*/
public abstract void onHostPause(Activity activity);
/**
* Use this method when the activity resumes to enable invoking the back button directly from JS.
*
@ -117,8 +131,21 @@ public abstract class ReactInstanceManager {
/**
* Call this from {@link Activity#onDestroy()}. This notifies any listening modules so they can do
* any necessary cleanup.
*
* @deprecated use {@link #onHostDestroy(Activity)} instead
*/
@Deprecated
public abstract void onHostDestroy();
/**
* 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
*/
public abstract void onHostDestroy(Activity activity);
public abstract void onActivityResult(int requestCode, int resultCode, Intent data);
public abstract void showDevOptionsDialog();

View File

@ -489,6 +489,17 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
moveToBeforeResumeLifecycleState();
}
@Override
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.
*
@ -525,6 +536,13 @@ import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
mCurrentActivity = null;
}
@Override
public void onHostDestroy(Activity activity) {
if (activity == mCurrentActivity) {
onHostDestroy();
}
}
@Override
public void destroy() {
UiThreadUtil.assertOnUiThread();

View File

@ -10,23 +10,39 @@
package com.facebook.react.bridge;
/**
* Listener for receiving activity/service lifecycle events.
* Listener for receiving activity lifecycle events.
*
* When multiple activities share a react instance, only the most recent one's lifecycle events get
* forwarded to listeners. Consider the following scenarios:
*
* 1. Navigating from Activity A to B will trigger two events: A#onHostPause and B#onHostResume. Any
* subsequent lifecycle events coming from Activity A, such as onHostDestroy, will be ignored.
* 2. Navigating back from Activity B to Activity A will trigger the same events: B#onHostPause and
* A#onHostResume. Any subsequent events coming from Activity B, such as onHostDestroy, are
* ignored.
* 3. Navigating back from Activity A to a non-React Activity or to the home screen will trigger two
* events: onHostPause and onHostDestroy.
* 4. Navigating from Activity A to a non-React Activity B will trigger one event: onHostPause.
* Later, if Activity A is destroyed (e.g. because of resource contention), onHostDestroy is
* triggered.
*/
public interface LifecycleEventListener {
/**
* Called when host (activity/service) receives resume event (e.g. {@link Activity#onResume}
* Called when host activity receives resume event (e.g. {@link Activity#onResume}. Always called
* for the most current activity.
*/
void onHostResume();
/**
* Called when host (activity/service) receives pause event (e.g. {@link Activity#onPause}
* Called when host activity receives pause event (e.g. {@link Activity#onPause}. Always called
* for the most current activity.
*/
void onHostPause();
/**
* Called when host (activity/service) receives destroy event (e.g. {@link Activity#onDestroy}
* Called when host activity receives destroy event (e.g. {@link Activity#onDestroy}. Only called
* for the last React activity to be destroyed.
*/
void onHostDestroy();
}