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
This commit is contained in:
Adam Powell
2016-04-21 11:04:41 -07:00
parent 54a6394cf9
commit a9bab98d70
2 changed files with 41 additions and 18 deletions

View File

@@ -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) {

View File

@@ -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);