From a9bab98d7014aefc81adf3fcb689242b651d46f1 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Thu, 21 Apr 2016 11:04:41 -0700 Subject: [PATCH] Move child FragmentManager shutdown for retained fragments Some apps out there do some creative things in terms of calling their own onDetach methods from elsewhere. Perform the child FragmentManager shutdown for retained fragments in a package-level perform method instead of in onDetach itself. Also mark all existing fragment methods with @CallSuper. Bug 28293847 Change-Id: I343b610f8d427fe5cabd523a78fbf0e6f3b34c57 --- core/java/android/app/Fragment.java | 52 +++++++++++++++++----- core/java/android/app/FragmentManager.java | 7 +-- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index a221c985428ee..f4fe1eb1cc6ee 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -17,6 +17,7 @@ package android.app; import android.animation.Animator; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; @@ -1335,6 +1336,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead. */ @Deprecated + @CallSuper public void onInflate(AttributeSet attrs, Bundle savedInstanceState) { mCalled = true; } @@ -1381,6 +1383,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @param savedInstanceState If the fragment is being re-created from * a previous saved state, this is the state. */ + @CallSuper public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) { onInflate(attrs, savedInstanceState); mCalled = true; @@ -1421,6 +1424,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead. */ @Deprecated + @CallSuper public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) { mCalled = true; } @@ -1429,6 +1433,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * Called when a fragment is first attached to its context. * {@link #onCreate(Bundle)} will be called after this. */ + @CallSuper public void onAttach(Context context) { mCalled = true; final Activity hostActivity = mHost == null ? null : mHost.getActivity(); @@ -1442,6 +1447,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @deprecated Use {@link #onAttach(Context)} instead. */ @Deprecated + @CallSuper public void onAttach(Activity activity) { mCalled = true; } @@ -1473,6 +1479,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @param savedInstanceState If the fragment is being re-created from * a previous saved state, this is the state. */ + @CallSuper public void onCreate(@Nullable Bundle savedInstanceState) { mCalled = true; final Context context = getContext(); @@ -1558,6 +1565,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @param savedInstanceState If the fragment is being re-created from * a previous saved state, this is the state. */ + @CallSuper public void onActivityCreated(@Nullable Bundle savedInstanceState) { mCalled = true; } @@ -1573,6 +1581,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * @param savedInstanceState If the fragment is being re-created from * a previous saved state, this is the state. */ + @CallSuper public void onViewStateRestored(Bundle savedInstanceState) { mCalled = true; } @@ -1582,6 +1591,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * tied to {@link Activity#onStart() Activity.onStart} of the containing * Activity's lifecycle. */ + @CallSuper public void onStart() { mCalled = true; @@ -1603,6 +1613,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * tied to {@link Activity#onResume() Activity.onResume} of the containing * Activity's lifecycle. */ + @CallSuper public void onResume() { mCalled = true; } @@ -1648,6 +1659,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { } + @CallSuper public void onConfigurationChanged(Configuration newConfig) { mCalled = true; } @@ -1657,6 +1669,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * tied to {@link Activity#onPause() Activity.onPause} of the containing * Activity's lifecycle. */ + @CallSuper public void onPause() { mCalled = true; } @@ -1666,14 +1679,17 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * tied to {@link Activity#onStop() Activity.onStop} of the containing * Activity's lifecycle. */ + @CallSuper public void onStop() { mCalled = true; } + @CallSuper public void onLowMemory() { mCalled = true; } + @CallSuper public void onTrimMemory(int level) { mCalled = true; } @@ -1687,6 +1703,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * non-null view. Internally it is called after the view's state has * been saved but before it has been removed from its parent. */ + @CallSuper public void onDestroyView() { mCalled = true; } @@ -1695,6 +1712,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * Called when the fragment is no longer in use. This is called * after {@link #onStop()} and before {@link #onDetach()}. */ + @CallSuper public void onDestroy() { mCalled = true; //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager @@ -1743,20 +1761,9 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * Activity re-creation (see {@link #setRetainInstance(boolean)}), in which case it is called * after {@link #onStop()}. */ + @CallSuper public void onDetach() { mCalled = true; - - // Destroy the child FragmentManager if we still have it here. - // We won't unless we're retaining our instance and if we do, - // our child FragmentManager instance state will have already been saved. - if (mChildFragmentManager != null) { - if (!mRetaining) { - throw new IllegalStateException("Child FragmentManager of " + this + " was not " - + " destroyed and this fragment is not retaining instance"); - } - mChildFragmentManager.dispatchDestroy(); - mChildFragmentManager = null; - } } /** @@ -2568,6 +2575,27 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mChildFragmentManager = null; } + void performDetach() { + mCalled = false; + onDetach(); + if (!mCalled) { + throw new SuperNotCalledException("Fragment " + this + + " did not call through to super.onDetach()"); + } + + // Destroy the child FragmentManager if we still have it here. + // We won't unless we're retaining our instance and if we do, + // our child FragmentManager instance state will have already been saved. + if (mChildFragmentManager != null) { + if (!mRetaining) { + throw new IllegalStateException("Child FragmentManager of " + this + " was not " + + " destroyed and this fragment is not retaining instance"); + } + mChildFragmentManager.dispatchDestroy(); + mChildFragmentManager = null; + } + } + private static Transition loadTransition(Context context, TypedArray typedArray, Transition currentValue, Transition defaultValue, int id) { if (currentValue != defaultValue) { diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index b1dda7f2a3fd6..d79538558b9a8 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1117,12 +1117,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate f.mState = Fragment.INITIALIZING; } - f.mCalled = false; - f.onDetach(); - if (!f.mCalled) { - throw new SuperNotCalledException("Fragment " + f - + " did not call through to super.onDetach()"); - } + f.performDetach(); if (!keepActive) { if (!f.mRetaining) { makeInactive(f);