Skip to content

Commit 635c60a

Browse files
committed
Add API for deferring fragment start.
Fragments now have the setDeferStart method to signal that a fragment has lower priority than others. Deferred start fragments will not always be started immediately; they will be started once any loaders have finished servicing any outstanding requests. This is useful if any attached fragments are not immediately visible and can wait to start until later. Disabling deferStart on a fragment that is waiting for a deferred start will start it immediately. Start. Change-Id: Ia1f004877ca5e88d4f10147d21c7e2e97f141c34
1 parent 6e03b22 commit 635c60a

File tree

4 files changed

+78
-2
lines changed

4 files changed

+78
-2
lines changed

api/current.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3245,6 +3245,7 @@ package android.app {
32453245
method public final boolean isInLayout();
32463246
method public final boolean isRemoving();
32473247
method public final boolean isResumed();
3248+
method public boolean isStartDeferred();
32483249
method public final boolean isVisible();
32493250
method public void onActivityCreated(android.os.Bundle);
32503251
method public void onActivityResult(int, int, android.content.Intent);
@@ -3280,6 +3281,7 @@ package android.app {
32803281
method public void setInitialSavedState(android.app.Fragment.SavedState);
32813282
method public void setMenuVisibility(boolean);
32823283
method public void setRetainInstance(boolean);
3284+
method public void setStartDeferred(boolean);
32833285
method public void setTargetFragment(android.app.Fragment, int);
32843286
method public void startActivity(android.content.Intent);
32853287
method public void startActivityForResult(android.content.Intent, int);

core/java/android/app/Fragment.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
339339
private static final HashMap<String, Class<?>> sClassMap =
340340
new HashMap<String, Class<?>>();
341341

342+
static final int INVALID_STATE = -1; // Invalid state used as a null value.
342343
static final int INITIALIZING = 0; // Not yet created.
343344
static final int CREATED = 1; // Created.
344345
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
@@ -403,7 +404,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
403404
// The fragment manager we are associated with. Set as soon as the
404405
// fragment is used in a transaction; cleared after it has been removed
405406
// from all transactions.
406-
FragmentManager mFragmentManager;
407+
FragmentManagerImpl mFragmentManager;
407408

408409
// Activity this fragment is attached to.
409410
Activity mActivity;
@@ -453,6 +454,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
453454
// The View generated for this fragment.
454455
View mView;
455456

457+
// Whether this fragment should defer starting until after other fragments
458+
// have been started and their loaders are finished.
459+
boolean mDeferStart;
460+
456461
LoaderManagerImpl mLoaderManager;
457462
boolean mLoadersStarted;
458463
boolean mCheckedForLoaderManager;
@@ -909,6 +914,34 @@ public void setMenuVisibility(boolean menuVisible) {
909914
}
910915
}
911916

917+
/**
918+
* Set whether this fragment should enter the started state as normal or if
919+
* start should be deferred until a system-determined convenient time, such
920+
* as after any loaders have completed their work.
921+
*
922+
* <p>This option is not sticky across fragment starts; after a deferred start
923+
* completes this option will be set to false.</p>
924+
*
925+
* @param deferResume true if this fragment can defer its resume until after others
926+
*/
927+
public void setStartDeferred(boolean deferResume) {
928+
if (mDeferStart && !deferResume) {
929+
mFragmentManager.performPendingDeferredStart(this);
930+
}
931+
mDeferStart = deferResume;
932+
}
933+
934+
/**
935+
* Returns true if this fragment's move to the started state has been deferred.
936+
* If this returns true it will be started once other fragments' loaders
937+
* have finished running.
938+
*
939+
* @return true if this fragment's start has been deferred.
940+
*/
941+
public boolean isStartDeferred() {
942+
return mDeferStart;
943+
}
944+
912945
/**
913946
* Return the LoaderManager for this fragment, creating it if needed.
914947
*/

core/java/android/app/FragmentManager.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,13 @@ Animator loadAnimator(Fragment fragment, int transit, boolean enter,
709709
return AnimatorInflater.loadAnimator(mActivity, anim);
710710
}
711711

712+
public void performPendingDeferredStart(Fragment f) {
713+
if (f.mDeferStart) {
714+
f.mDeferStart = false;
715+
moveToState(f, mCurState, 0, 0);
716+
}
717+
}
718+
712719
void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
713720
// Fragments that are not currently added will sit in the onCreate() state.
714721
if (!f.mAdded && newState > Fragment.CREATED) {
@@ -718,7 +725,10 @@ void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
718725
// While removing a fragment, we can't change it to a higher state.
719726
newState = f.mState;
720727
}
721-
728+
// Defer start if requested; don't allow it to move to STARTED or higher.
729+
if (f.mDeferStart && newState > Fragment.STOPPED) {
730+
newState = Fragment.STOPPED;
731+
}
722732
if (f.mState < newState) {
723733
// For fragments that are created from a layout, when restoring from
724734
// state we don't want to allow them to be created until they are
@@ -992,20 +1002,37 @@ void moveToState(int newState, int transit, int transitStyle, boolean always) {
9921002

9931003
mCurState = newState;
9941004
if (mActive != null) {
1005+
boolean loadersRunning = false;
9951006
for (int i=0; i<mActive.size(); i++) {
9961007
Fragment f = mActive.get(i);
9971008
if (f != null) {
9981009
moveToState(f, newState, transit, transitStyle);
1010+
if (f.mLoaderManager != null) {
1011+
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1012+
}
9991013
}
10001014
}
10011015

1016+
if (!loadersRunning) {
1017+
startPendingDeferredFragments();
1018+
}
1019+
10021020
if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
10031021
mActivity.invalidateOptionsMenu();
10041022
mNeedMenuInvalidate = false;
10051023
}
10061024
}
10071025
}
10081026

1027+
void startPendingDeferredFragments() {
1028+
for (int i=0; i<mActive.size(); i++) {
1029+
Fragment f = mActive.get(i);
1030+
if (f != null) {
1031+
performPendingDeferredStart(f);
1032+
}
1033+
}
1034+
}
1035+
10091036
void makeActive(Fragment f) {
10101037
if (f.mIndex >= 0) {
10111038
return;

core/java/android/app/LoaderManager.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ void destroy() {
418418
info.destroy();
419419
mInactiveLoaders.remove(mId);
420420
}
421+
422+
if (!hasRunningLoaders() && mActivity != null) {
423+
mActivity.mFragments.startPendingDeferredFragments();
424+
}
421425
}
422426

423427
void callOnLoadFinished(Loader<Object> loader, Object data) {
@@ -820,4 +824,14 @@ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[]
820824
}
821825
}
822826
}
827+
828+
public boolean hasRunningLoaders() {
829+
boolean loadersRunning = false;
830+
final int count = mLoaders.size();
831+
for (int i = 0; i < count; i++) {
832+
final LoaderInfo li = mLoaders.valueAt(i);
833+
loadersRunning |= li.mStarted && !li.mDeliveredData;
834+
}
835+
return loadersRunning;
836+
}
823837
}

0 commit comments

Comments
 (0)