Force react applications to be explicitly unmounted
Summary: We previously were unmounting the react application unconditionally when the ReactRootView#onDetachedFromWindow. This is nice in that it automatically allows us to reclaim memory, but there are many scenarios where a ReactRootView can be embedded in another piece of UI that detaches its children as part of its normal function (e.g. ListView, RecyclerView, ViewPager, etc). As such, we will now enforce that the hosting Activity/Fragment/??? explicitly calls unmountReactApplication in the same way it calls startReactApplication. For Applications extending ReactActivity/AbstractReactActivity, this will happen automatically in onDestroy. Reviewed By: foghina Differential Revision: D3265161 fb-gh-sync-id: 4d49b0c41256213f00874f57e784aa8741dbf394 fbshipit-source-id: 4d49b0c41256213f00874f57e784aa8741dbf394
This commit is contained in:
parent
f7ce0c1c2f
commit
54f7ae1c02
|
@ -27,6 +27,7 @@ public abstract class ReactActivity extends Activity implements DefaultHardwareB
|
|||
"Overlay permissions needs to be granted in order for react native apps to run in dev mode";
|
||||
|
||||
private @Nullable ReactInstanceManager mReactInstanceManager;
|
||||
private @Nullable ReactRootView mReactRootView;
|
||||
private LifecycleState mLifecycleState = LifecycleState.BEFORE_RESUME;
|
||||
private boolean mDoRefresh = false;
|
||||
|
||||
|
@ -138,7 +139,7 @@ public abstract class ReactActivity extends Activity implements DefaultHardwareB
|
|||
}
|
||||
|
||||
mReactInstanceManager = createReactInstanceManager();
|
||||
ReactRootView mReactRootView = createRootView();
|
||||
mReactRootView = createRootView();
|
||||
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
|
||||
setContentView(mReactRootView);
|
||||
}
|
||||
|
@ -169,6 +170,9 @@ public abstract class ReactActivity extends Activity implements DefaultHardwareB
|
|||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
mReactRootView.unmountReactApplication();
|
||||
mReactRootView = null;
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.destroy();
|
||||
}
|
||||
|
|
|
@ -165,13 +165,18 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
|||
// No-op since UIManagerModule handles actually laying out children.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (mIsAttachedToInstance) {
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
if (mReactInstanceManager != null && mIsAttachedToInstance) {
|
||||
mReactInstanceManager.detachRootView(this);
|
||||
mIsAttachedToInstance = false;
|
||||
if (mIsAttachedToInstance) {
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(getKeyboardListener());
|
||||
}
|
||||
}
|
||||
|
@ -217,6 +222,19 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmount the react application at this root view, reclaiming any JS memory associated with that
|
||||
* application. If {@link #startReactApplication} is called, this method must be called before the
|
||||
* ReactRootView is garbage collected (typically in your Activity's onDestroy, or in your Fragment's
|
||||
* onDestroyView).
|
||||
*/
|
||||
public void unmountReactApplication() {
|
||||
if (mReactInstanceManager != null && mIsAttachedToInstance) {
|
||||
mReactInstanceManager.detachRootView(this);
|
||||
mIsAttachedToInstance = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ String getJSModuleName() {
|
||||
return Assertions.assertNotNull(mJSModuleName);
|
||||
}
|
||||
|
@ -252,6 +270,17 @@ public class ReactRootView extends SizeMonitoringFrameLayout implements RootView
|
|||
getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
Assertions.assertCondition(
|
||||
!mIsAttachedToInstance,
|
||||
"The application this ReactRootView was rendering was not unmounted before the ReactRootView " +
|
||||
"was garbage collected. This usually means that your application is leaking large amounts of " +
|
||||
"memory. To solve this, make sure to call ReactRootView#unmountReactApplication in the onDestroy() " +
|
||||
"of your hosting Activity or in the onDestroyView() of your hosting Fragment.");
|
||||
}
|
||||
|
||||
private class KeyboardListener implements ViewTreeObserver.OnGlobalLayoutListener {
|
||||
private final Rect mVisibleViewArea;
|
||||
private final int mMinKeyboardHeightDetected;
|
||||
|
|
Loading…
Reference in New Issue