OSDN Git Service

Change the "start deferred" fragment API to "user visible hint"
authorAdam Powell <adamp@google.com>
Mon, 7 Nov 2011 18:45:34 +0000 (10:45 -0800)
committerAdam Powell <adamp@google.com>
Mon, 7 Nov 2011 21:47:13 +0000 (13:47 -0800)
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

api/current.txt
core/java/android/app/Fragment.java
core/java/android/app/FragmentManager.java

index 4e41c29..cc7f079 100644 (file)
@@ -3235,6 +3235,7 @@ package android.app {
     method public final android.app.Fragment getTargetFragment();
     method public final int getTargetRequestCode();
     method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
     method public android.view.View getView();
     method public final int hashCode();
     method public static android.app.Fragment instantiate(android.content.Context, java.lang.String);
@@ -3245,7 +3246,6 @@ package android.app {
     method public final boolean isInLayout();
     method public final boolean isRemoving();
     method public final boolean isResumed();
-    method public boolean isStartDeferred();
     method public final boolean isVisible();
     method public void onActivityCreated(android.os.Bundle);
     method public void onActivityResult(int, int, android.content.Intent);
@@ -3281,8 +3281,8 @@ package android.app {
     method public void setInitialSavedState(android.app.Fragment.SavedState);
     method public void setMenuVisibility(boolean);
     method public void setRetainInstance(boolean);
-    method public void setStartDeferred(boolean);
     method public void setTargetFragment(android.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
     method public void startActivity(android.content.Intent);
     method public void startActivityForResult(android.content.Intent, int);
     method public void unregisterForContextMenu(android.view.View);
index 9b01b7f..473a2d1 100644 (file)
@@ -458,6 +458,9 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
     // have been started and their loaders are finished.
     boolean mDeferStart;
 
+    // Hint provided by the app that this fragment is currently visible to the user.
+    boolean mUserVisibleHint = true;
+
     LoaderManagerImpl mLoaderManager;
     boolean mLoadersStarted;
     boolean mCheckedForLoaderManager;
@@ -915,31 +918,32 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
     }
 
     /**
-     * Set whether this fragment should enter the started state as normal or if
-     * start should be deferred until a system-determined convenient time, such
-     * as after any loaders have completed their work.
+     * Set a hint to the system about whether this fragment's UI is currently visible
+     * to the user. This hint defaults to true and is persistent across fragment instance
+     * state save and restore.
      *
-     * <p>This option is not sticky across fragment starts; after a deferred start
-     * completes this option will be set to false.</p>
+     * <p>An app may set this to false to indicate that the fragment's UI is
+     * scrolled out of visibility or is otherwise not directly visible to the user.
+     * This may be used by the system to prioritize operations such as fragment lifecycle updates
+     * or loader ordering behavior.</p>
      *
-     * @param deferResume true if this fragment can defer its resume until after others
+     * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
+     *                        false if it is not.
      */
-    public void setStartDeferred(boolean deferResume) {
-        if (mDeferStart && !deferResume) {
+    public void setUserVisibleHint(boolean isVisibleToUser) {
+        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) {
             mFragmentManager.performPendingDeferredStart(this);
         }
-        mDeferStart = deferResume;
+        mUserVisibleHint = isVisibleToUser;
+        mDeferStart = !isVisibleToUser;
     }
 
     /**
-     * Returns true if this fragment's move to the started state has been deferred.
-     * If this returns true it will be started once other fragments' loaders
-     * have finished running.
-     *
-     * @return true if this fragment's start has been deferred.
+     * @return The current value of the user-visible hint on this fragment.
+     * @see #setUserVisibleHint(boolean)
      */
-    public boolean isStartDeferred() {
-        return mDeferStart;
+    public boolean getUserVisibleHint() {
+        return mUserVisibleHint;
     }
 
     /**
@@ -1477,7 +1481,8 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
                 writer.print(" mMenuVisible="); writer.print(mMenuVisible);
                 writer.print(" mHasMenu="); writer.println(mHasMenu);
         writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance);
-                writer.print(" mRetaining="); writer.println(mRetaining);
+                writer.print(" mRetaining="); writer.print(mRetaining);
+                writer.print(" mUserVisibleHint="); writer.println(mUserVisibleHint);
         if (mFragmentManager != null) {
             writer.print(prefix); writer.print("mFragmentManager=");
                     writer.println(mFragmentManager);
index c4ba778..a8c9cba 100644 (file)
@@ -382,6 +382,7 @@ final class FragmentManagerImpl extends FragmentManager {
     static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
     static final String TARGET_STATE_TAG = "android:target_state";
     static final String VIEW_STATE_TAG = "android:view_state";
+    static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint";
 
     ArrayList<Runnable> mPendingActions;
     Runnable[] mTmpActions;
@@ -406,6 +407,7 @@ final class FragmentManagerImpl extends FragmentManager {
     boolean mStateSaved;
     boolean mDestroyed;
     String mNoTransactionsBecause;
+    boolean mHavePendingDeferredStart;
     
     // Temporary vars for state save and restore.
     Bundle mStateBundle = null;
@@ -711,6 +713,11 @@ final class FragmentManagerImpl extends FragmentManager {
     
     public void performPendingDeferredStart(Fragment f) {
         if (f.mDeferStart) {
+            if (mExecutingActions) {
+                // Wait until we're done executing our pending transactions
+                mHavePendingDeferredStart = true;
+                return;
+            }
             f.mDeferStart = false;
             moveToState(f, mCurState, 0, 0);
         }
@@ -757,6 +764,14 @@ final class FragmentManagerImpl extends FragmentManager {
                             f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                     FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                         }
+                        f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
+                                FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
+                        if (!f.mUserVisibleHint) {
+                            f.mDeferStart = true;
+                            if (newState > Fragment.STOPPED) {
+                                newState = Fragment.STOPPED;
+                            }
+                        }
                     }
                     f.mActivity = mActivity;
                     f.mFragmentManager = mActivity.mFragments;
@@ -1343,7 +1358,7 @@ final class FragmentManagerImpl extends FragmentManager {
             
             synchronized (this) {
                 if (mPendingActions == null || mPendingActions.size() == 0) {
-                    return didSomething;
+                    break;
                 }
                 
                 numActions = mPendingActions.size();
@@ -1363,8 +1378,23 @@ final class FragmentManagerImpl extends FragmentManager {
             mExecutingActions = false;
             didSomething = true;
         }
+
+        if (mHavePendingDeferredStart) {
+            boolean loadersRunning = false;
+            for (int i=0; i<mActive.size(); i++) {
+                Fragment f = mActive.get(i);
+                if (f != null && f.mLoaderManager != null) {
+                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
+                }
+            }
+            if (!loadersRunning) {
+                mHavePendingDeferredStart = false;
+                startPendingDeferredFragments();
+            }
+        }
+        return didSomething;
     }
-    
+
     void reportBackStackChanged() {
         if (mBackStackChangeListeners != null) {
             for (int i=0; i<mBackStackChangeListeners.size(); i++) {
@@ -1500,6 +1530,10 @@ final class FragmentManagerImpl extends FragmentManager {
             result.putSparseParcelableArray(
                     FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
         }
+        if (!f.mUserVisibleHint) {
+            // Only add this if it's not the default value
+            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
+        }
 
         return result;
     }