diff --git a/api/current.txt b/api/current.txt index 47a0efe6f275e..f8ff526de8d7c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4516,6 +4516,7 @@ package android.app { method public final boolean isInLayout(); method public final boolean isRemoving(); method public final boolean isResumed(); + method public final boolean isStateSaved(); method public final boolean isVisible(); method public void onActivityCreated(android.os.Bundle); method public void onActivityResult(int, int, android.content.Intent); @@ -4754,6 +4755,7 @@ package android.app { method public abstract android.app.FragmentTransaction hide(android.app.Fragment); method public abstract boolean isAddToBackStackAllowed(); method public abstract boolean isEmpty(); + method public abstract android.app.FragmentTransaction postOnCommit(java.lang.Runnable); method public abstract android.app.FragmentTransaction remove(android.app.Fragment); method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment); method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment, java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index d079e99c13b42..a24fe55ba9d1f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4676,6 +4676,7 @@ package android.app { method public final boolean isInLayout(); method public final boolean isRemoving(); method public final boolean isResumed(); + method public final boolean isStateSaved(); method public final boolean isVisible(); method public void onActivityCreated(android.os.Bundle); method public void onActivityResult(int, int, android.content.Intent); @@ -4914,6 +4915,7 @@ package android.app { method public abstract android.app.FragmentTransaction hide(android.app.Fragment); method public abstract boolean isAddToBackStackAllowed(); method public abstract boolean isEmpty(); + method public abstract android.app.FragmentTransaction postOnCommit(java.lang.Runnable); method public abstract android.app.FragmentTransaction remove(android.app.Fragment); method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment); method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment, java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 54379bf58da31..219396a8b38c7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4526,6 +4526,7 @@ package android.app { method public final boolean isInLayout(); method public final boolean isRemoving(); method public final boolean isResumed(); + method public final boolean isStateSaved(); method public final boolean isVisible(); method public void onActivityCreated(android.os.Bundle); method public void onActivityResult(int, int, android.content.Intent); @@ -4764,6 +4765,7 @@ package android.app { method public abstract android.app.FragmentTransaction hide(android.app.Fragment); method public abstract boolean isAddToBackStackAllowed(); method public abstract boolean isEmpty(); + method public abstract android.app.FragmentTransaction postOnCommit(java.lang.Runnable); method public abstract android.app.FragmentTransaction remove(android.app.Fragment); method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment); method public abstract android.app.FragmentTransaction replace(int, android.app.Fragment, java.lang.String); diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index a27775ea4d851..66b2355dcb6e6 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -220,6 +220,8 @@ final class BackStackRecord extends FragmentTransaction implements int mIndex = -1; boolean mAllowOptimization; + ArrayList mCommitRunnables; + int mBreadCrumbTitleRes; CharSequence mBreadCrumbTitleText; int mBreadCrumbShortTitleRes; @@ -620,6 +622,28 @@ final class BackStackRecord extends FragmentTransaction implements } } + @Override + public FragmentTransaction postOnCommit(Runnable runnable) { + if (runnable == null) { + throw new IllegalArgumentException("runnable cannot be null"); + } + disallowAddToBackStack(); + if (mCommitRunnables == null) { + mCommitRunnables = new ArrayList<>(); + } + mCommitRunnables.add(runnable); + return this; + } + + public void runOnCommitRunnables() { + if (mCommitRunnables != null) { + for (int i = 0, N = mCommitRunnables.size(); i < N; i++) { + mCommitRunnables.get(i).run(); + } + mCommitRunnables = null; + } + } + public int commit() { return commitInternal(false); } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 612998dd51583..108ab71923af2 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -714,14 +714,20 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } /** - * Supply the construction arguments for this fragment. This can only - * be called before the fragment has been attached to its activity; that - * is, you should call it immediately after constructing the fragment. The - * arguments supplied here will be retained across fragment destroy and + * Supply the construction arguments for this fragment. + * The arguments supplied here will be retained across fragment destroy and * creation. + * + *

This method cannot be called if the fragment is added to a FragmentManager and + * if {@link #isStateSaved()} would return true. Prior to {@link Build.VERSION_CODES#O}, + * this method may only be called if the fragment has not yet been added to a FragmentManager. + *

*/ public void setArguments(Bundle args) { - if (mIndex >= 0) { + // The isStateSaved requirement below was only added in Android O and is compatible + // because it loosens previous requirements rather than making them more strict. + // See method javadoc. + if (mIndex >= 0 && isStateSaved()) { throw new IllegalStateException("Fragment already active"); } mArguments = args; @@ -734,6 +740,21 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene return mArguments; } + /** + * Returns true if this fragment is added and its state has already been saved + * by its host. Any operations that would change saved state should not be performed + * if this method returns true, and some operations such as {@link #setArguments(Bundle)} + * will fail. + * + * @return true if this fragment's state has already been saved by its host + */ + public final boolean isStateSaved() { + if (mFragmentManager == null) { + return false; + } + return mFragmentManager.isStateSaved(); + } + /** * Set the initial saved state that this Fragment should restore itself * from when first being constructed, as returned by diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index b0150bdf19360..4f68ec71c5ddb 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1787,6 +1787,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } + public boolean isStateSaved() { + return mStateSaved; + } + /** * Adds an action to the queue of pending actions. * @@ -2108,6 +2112,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate freeBackStackIndex(record.mIndex); record.mIndex = -1; } + record.runOnCommitRunnables(); } if (addToBackStack) { diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index 07a313c9eeaf6..c938aa607e105 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -303,6 +303,22 @@ public abstract class FragmentTransaction { */ public abstract FragmentTransaction setAllowOptimization(boolean allowOptimization); + /** + * Add a Runnable to this transaction that will be run after this transaction has + * been committed. If fragment transactions are {@link #setAllowOptimization(boolean) optimized} + * this may be after other subsequent fragment operations have also taken place, or operations + * in this transaction may have been optimized out due to the presence of a subsequent + * fragment transaction in the batch. + * + *

postOnCommit may not be used with transactions + * {@link #addToBackStack(String) added to the back stack} as Runnables cannot be persisted + * with back stack state.

+ * + * @param runnable Runnable to add + * @return this FragmentTransaction + */ + public abstract FragmentTransaction postOnCommit(Runnable runnable); + /** * Schedules a commit of this transaction. The commit does * not happen immediately; it will be scheduled as work on the main thread