Skip to content

Commit 78fed9b

Browse files
committed
Change the "start deferred" fragment API to "user visible hint"
Allow a fragment to set a hint of whether or not it is currently user visible. This will be used implicitly to defer the start of fragments that are not user visible until the loaders for visible fragments have run. This hint defaults to true. Change-Id: Id1349d319886a277ef07301f64f7b9e12c8729bf
1 parent 19c86ca commit 78fed9b

File tree

3 files changed

+60
-21
lines changed

3 files changed

+60
-21
lines changed

api/current.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,7 @@ package android.app {
32353235
method public final android.app.Fragment getTargetFragment();
32363236
method public final int getTargetRequestCode();
32373237
method public final java.lang.CharSequence getText(int);
3238+
method public boolean getUserVisibleHint();
32383239
method public android.view.View getView();
32393240
method public final int hashCode();
32403241
method public static android.app.Fragment instantiate(android.content.Context, java.lang.String);
@@ -3245,7 +3246,6 @@ package android.app {
32453246
method public final boolean isInLayout();
32463247
method public final boolean isRemoving();
32473248
method public final boolean isResumed();
3248-
method public boolean isStartDeferred();
32493249
method public final boolean isVisible();
32503250
method public void onActivityCreated(android.os.Bundle);
32513251
method public void onActivityResult(int, int, android.content.Intent);
@@ -3281,8 +3281,8 @@ package android.app {
32813281
method public void setInitialSavedState(android.app.Fragment.SavedState);
32823282
method public void setMenuVisibility(boolean);
32833283
method public void setRetainInstance(boolean);
3284-
method public void setStartDeferred(boolean);
32853284
method public void setTargetFragment(android.app.Fragment, int);
3285+
method public void setUserVisibleHint(boolean);
32863286
method public void startActivity(android.content.Intent);
32873287
method public void startActivityForResult(android.content.Intent, int);
32883288
method public void unregisterForContextMenu(android.view.View);

core/java/android/app/Fragment.java

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,9 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
458458
// have been started and their loaders are finished.
459459
boolean mDeferStart;
460460

461+
// Hint provided by the app that this fragment is currently visible to the user.
462+
boolean mUserVisibleHint = true;
463+
461464
LoaderManagerImpl mLoaderManager;
462465
boolean mLoadersStarted;
463466
boolean mCheckedForLoaderManager;
@@ -915,31 +918,32 @@ public void setMenuVisibility(boolean menuVisible) {
915918
}
916919

917920
/**
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+
* Set a hint to the system about whether this fragment's UI is currently visible
922+
* to the user. This hint defaults to true and is persistent across fragment instance
923+
* state save and restore.
921924
*
922-
* <p>This option is not sticky across fragment starts; after a deferred start
923-
* completes this option will be set to false.</p>
925+
* <p>An app may set this to false to indicate that the fragment's UI is
926+
* scrolled out of visibility or is otherwise not directly visible to the user.
927+
* This may be used by the system to prioritize operations such as fragment lifecycle updates
928+
* or loader ordering behavior.</p>
924929
*
925-
* @param deferResume true if this fragment can defer its resume until after others
930+
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
931+
* false if it is not.
926932
*/
927-
public void setStartDeferred(boolean deferResume) {
928-
if (mDeferStart && !deferResume) {
933+
public void setUserVisibleHint(boolean isVisibleToUser) {
934+
if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
929935
mFragmentManager.performPendingDeferredStart(this);
930936
}
931-
mDeferStart = deferResume;
937+
mUserVisibleHint = isVisibleToUser;
938+
mDeferStart = !isVisibleToUser;
932939
}
933940

934941
/**
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.
942+
* @return The current value of the user-visible hint on this fragment.
943+
* @see #setUserVisibleHint(boolean)
940944
*/
941-
public boolean isStartDeferred() {
942-
return mDeferStart;
945+
public boolean getUserVisibleHint() {
946+
return mUserVisibleHint;
943947
}
944948

945949
/**
@@ -1477,7 +1481,8 @@ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[]
14771481
writer.print(" mMenuVisible="); writer.print(mMenuVisible);
14781482
writer.print(" mHasMenu="); writer.println(mHasMenu);
14791483
writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance);
1480-
writer.print(" mRetaining="); writer.println(mRetaining);
1484+
writer.print(" mRetaining="); writer.print(mRetaining);
1485+
writer.print(" mUserVisibleHint="); writer.println(mUserVisibleHint);
14811486
if (mFragmentManager != null) {
14821487
writer.print(prefix); writer.print("mFragmentManager=");
14831488
writer.println(mFragmentManager);

core/java/android/app/FragmentManager.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ final class FragmentManagerImpl extends FragmentManager {
382382
static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
383383
static final String TARGET_STATE_TAG = "android:target_state";
384384
static final String VIEW_STATE_TAG = "android:view_state";
385+
static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
385386

386387
ArrayList<Runnable> mPendingActions;
387388
Runnable[] mTmpActions;
@@ -406,6 +407,7 @@ final class FragmentManagerImpl extends FragmentManager {
406407
boolean mStateSaved;
407408
boolean mDestroyed;
408409
String mNoTransactionsBecause;
410+
boolean mHavePendingDeferredStart;
409411

410412
// Temporary vars for state save and restore.
411413
Bundle mStateBundle = null;
@@ -711,6 +713,11 @@ Animator loadAnimator(Fragment fragment, int transit, boolean enter,
711713

712714
public void performPendingDeferredStart(Fragment f) {
713715
if (f.mDeferStart) {
716+
if (mExecutingActions) {
717+
// Wait until we're done executing our pending transactions
718+
mHavePendingDeferredStart = true;
719+
return;
720+
}
714721
f.mDeferStart = false;
715722
moveToState(f, mCurState, 0, 0);
716723
}
@@ -757,6 +764,14 @@ void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
757764
f.mTargetRequestCode = f.mSavedFragmentState.getInt(
758765
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
759766
}
767+
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
768+
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
769+
if (!f.mUserVisibleHint) {
770+
f.mDeferStart = true;
771+
if (newState > Fragment.STOPPED) {
772+
newState = Fragment.STOPPED;
773+
}
774+
}
760775
}
761776
f.mActivity = mActivity;
762777
f.mFragmentManager = mActivity.mFragments;
@@ -1343,7 +1358,7 @@ public boolean execPendingActions() {
13431358

13441359
synchronized (this) {
13451360
if (mPendingActions == null || mPendingActions.size() == 0) {
1346-
return didSomething;
1361+
break;
13471362
}
13481363

13491364
numActions = mPendingActions.size();
@@ -1363,8 +1378,23 @@ public boolean execPendingActions() {
13631378
mExecutingActions = false;
13641379
didSomething = true;
13651380
}
1381+
1382+
if (mHavePendingDeferredStart) {
1383+
boolean loadersRunning = false;
1384+
for (int i=0; i<mActive.size(); i++) {
1385+
Fragment f = mActive.get(i);
1386+
if (f != null && f.mLoaderManager != null) {
1387+
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
1388+
}
1389+
}
1390+
if (!loadersRunning) {
1391+
mHavePendingDeferredStart = false;
1392+
startPendingDeferredFragments();
1393+
}
1394+
}
1395+
return didSomething;
13661396
}
1367-
1397+
13681398
void reportBackStackChanged() {
13691399
if (mBackStackChangeListeners != null) {
13701400
for (int i=0; i<mBackStackChangeListeners.size(); i++) {
@@ -1500,6 +1530,10 @@ Bundle saveFragmentBasicState(Fragment f) {
15001530
result.putSparseParcelableArray(
15011531
FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
15021532
}
1533+
if (!f.mUserVisibleHint) {
1534+
// Only add this if it's not the default value
1535+
result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
1536+
}
15031537

15041538
return result;
15051539
}

0 commit comments

Comments
 (0)