diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-12-23 13:58:22 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-12-28 14:01:20 -0800 |
commit | d173fa3b1cb8e4294aba7564c0171894be6c3c24 (patch) | |
tree | aeba6e98ef80d95d291d0fd0874a0122e6220bbd /core/java/android/app | |
parent | 7d4b0062f8c7f530d289bfb017f0bced0ffa0f00 (diff) | |
download | frameworks_base-d173fa3b1cb8e4294aba7564c0171894be6c3c24.zip frameworks_base-d173fa3b1cb8e4294aba7564c0171894be6c3c24.tar.gz frameworks_base-d173fa3b1cb8e4294aba7564c0171894be6c3c24.tar.bz2 |
Possible fix to issue #3213749: NPE at...
...android.app.Fragment.startActivityForResult(Fragment.java)
Make sure to remove all pending messages when AbsListView is detached
from its window.
But... that's not enough.
It turns out that when a fragment's views are animating away, they of
course don't get detached until after the animation is done. However
the fragment itself is immediately destroyed, leaving its live views
still going after that.
Here's a possible solution: when fragment manager initiates an animation
on a fragment whose views are being removed, it makes note of that so
it can hold off on destroying the fragment until the animation is over.
There are a lot of interesting race conditions here, if further operations
happen on the fragment while it is being animated. I think the code here
does something sensible, and it does seem to work for the situations I
have tested, but it is hard to know all of the edge cases that may happen.
Change-Id: I4490ce8862a9bb714c7ea54baca3072c62126388
Diffstat (limited to 'core/java/android/app')
-rw-r--r-- | core/java/android/app/Fragment.java | 14 | ||||
-rw-r--r-- | core/java/android/app/FragmentManager.java | 159 |
2 files changed, 143 insertions, 30 deletions
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index f06f2cf..a920814 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -323,6 +323,15 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener int mState = INITIALIZING; + // Non-null if the fragment's view hierarchy is currently animating away, + // meaning we need to wait a bit on completely destroying it. This is the + // animation that is running. + Animator mAnimatingAway; + + // If mAnimatingAway != null, this is the state we should move to once the + // animation is done. + int mStateAfterAnimating; + // When instantiated from saved state, this is the saved state. Bundle mSavedFragmentState; SparseArray<Parcelable> mSavedViewState; @@ -1240,6 +1249,11 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener if (mView != null) { writer.print(prefix); writer.print("mView="); writer.println(mView); } + if (mAnimatingAway != null) { + writer.print(prefix); writer.print("mAnimatingAway="); writer.println(mAnimatingAway); + writer.print(prefix); writer.print("mStateAfterAnimating="); + writer.println(mStateAfterAnimating); + } if (mLoaderManager != null) { writer.print(prefix); writer.println("Loader Manager:"); mLoaderManager.dump(prefix + " ", fd, writer, args); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index fe2ebed..b5da010 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -38,6 +38,7 @@ import android.view.ViewGroup; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; /** * Interface for interacting with {@link Fragment} objects inside of an @@ -331,6 +332,7 @@ final class FragmentManagerImpl extends FragmentManager { boolean mNeedMenuInvalidate; boolean mStateSaved; + boolean mDestroyed; String mNoTransactionsBecause; // Temporary vars for state save and restore. @@ -473,23 +475,23 @@ final class FragmentManagerImpl extends FragmentManager { @Override public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { - if (mActive == null || mActive.size() <= 0) { - return; - } - - writer.print(prefix); writer.print("Active Fragments in "); - writer.print(Integer.toHexString(System.identityHashCode(this))); - writer.println(":"); - String innerPrefix = prefix + " "; - int N = mActive.size(); - for (int i=0; i<N; i++) { - Fragment f = mActive.get(i); - if (f != null) { - writer.print(prefix); writer.print(" #"); writer.print(i); - writer.print(": "); writer.println(f.toString()); - f.dump(innerPrefix, fd, writer, args); + int N; + if (mActive != null) { + N = mActive.size(); + if (N > 0) { + writer.print(prefix); writer.print("Active Fragments in "); + writer.print(Integer.toHexString(System.identityHashCode(this))); + writer.println(":"); + for (int i=0; i<N; i++) { + Fragment f = mActive.get(i); + writer.print(prefix); writer.print(" #"); writer.print(i); + writer.print(": "); writer.println(f); + if (f != null) { + f.dump(innerPrefix, fd, writer, args); + } + } } } @@ -505,6 +507,18 @@ final class FragmentManagerImpl extends FragmentManager { } } + if (mCreatedMenus != null) { + N = mCreatedMenus.size(); + if (N > 0) { + writer.print(prefix); writer.println("Fragments Created Menus:"); + for (int i=0; i<N; i++) { + Fragment f = mCreatedMenus.get(i); + writer.print(prefix); writer.print(" #"); writer.print(i); + writer.print(": "); writer.println(f.toString()); + } + } + } + if (mBackStack != null) { N = mBackStack.size(); if (N > 0) { @@ -517,6 +531,54 @@ final class FragmentManagerImpl extends FragmentManager { } } } + + synchronized (this) { + if (mBackStackIndices != null) { + N = mBackStackIndices.size(); + if (N > 0) { + writer.print(prefix); writer.println("Back Stack Indices:"); + for (int i=0; i<N; i++) { + BackStackRecord bs = mBackStackIndices.get(i); + writer.print(prefix); writer.print(" #"); writer.print(i); + writer.print(": "); writer.println(bs); + } + } + } + + if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { + writer.print(prefix); writer.print("mAvailBackStackIndices: "); + writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); + } + } + + if (mPendingActions != null) { + N = mPendingActions.size(); + if (N > 0) { + writer.print(prefix); writer.println("Pending Actions:"); + for (int i=0; i<N; i++) { + Runnable r = mPendingActions.get(i); + writer.print(prefix); writer.print(" #"); writer.print(i); + writer.print(": "); writer.println(r); + } + } + } + + writer.print(prefix); writer.println("FragmentManager misc state:"); + writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); + writer.print(" mStateSaved="); writer.print(mStateSaved); + writer.print(" mDestroyed="); writer.println(mDestroyed); + if (mNeedMenuInvalidate) { + writer.print(prefix); writer.print(" mNeedMenuInvalidate="); + writer.println(mNeedMenuInvalidate); + } + if (mNoTransactionsBecause != null) { + writer.print(prefix); writer.print(" mNoTransactionsBecause="); + writer.println(mNoTransactionsBecause); + } + if (mAvailIndices != null && mAvailIndices.size() > 0) { + writer.print(prefix); writer.print(" mAvailIndices: "); + writer.println(Arrays.toString(mAvailIndices.toArray())); + } } Animator loadAnimator(Fragment fragment, int transit, boolean enter, @@ -569,6 +631,14 @@ final class FragmentManagerImpl extends FragmentManager { } if (f.mState < newState) { + if (f.mAnimatingAway != null) { + // The fragment is currently being animated... but! Now we + // want to move our state back up. Give up on waiting for the + // animation, move to whatever the final state should be once + // the animation is done, and then we can proceed from there. + f.mAnimatingAway = null; + moveToState(f, f.mStateAfterAnimating, 0, 0); + } switch (f.mState) { case Fragment.INITIALIZING: if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); @@ -716,18 +786,26 @@ final class FragmentManagerImpl extends FragmentManager { } if (f.mView != null && f.mContainer != null) { Animator anim = null; - if (mCurState > Fragment.INITIALIZING) { + if (mCurState > Fragment.INITIALIZING && !mDestroyed) { anim = loadAnimator(f, transit, false, transitionStyle); } if (anim != null) { final ViewGroup container = f.mContainer; final View view = f.mView; + final Fragment fragment = f; container.startViewTransition(view); + f.mAnimatingAway = anim; + f.mStateAfterAnimating = newState; anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator anim) { container.endViewTransition(view); + if (fragment.mAnimatingAway != null) { + fragment.mAnimatingAway = null; + moveToState(fragment, fragment.mStateAfterAnimating, + 0, 0); + } } }); anim.setTarget(f.mView); @@ -741,25 +819,45 @@ final class FragmentManagerImpl extends FragmentManager { } case Fragment.CREATED: if (newState < Fragment.CREATED) { - if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); - if (!f.mRetaining) { + if (mDestroyed) { + if (f.mAnimatingAway != null) { + // The fragment's containing activity is + // being destroyed, but this fragment is + // currently animating away. Stop the + // animation right now -- it is not needed, + // and we can't wait any more on destroying + // the fragment. + f.mAnimatingAway = null; + f.mAnimatingAway.cancel(); + } + } + if (f.mAnimatingAway != null) { + // We are waiting for the fragment's view to finish + // animating away. Just make a note of the state + // the fragment now should move to once the animation + // is done. + f.mStateAfterAnimating = newState; + } else { + if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); + if (!f.mRetaining) { + f.mCalled = false; + f.onDestroy(); + if (!f.mCalled) { + throw new SuperNotCalledException("Fragment " + f + + " did not call through to super.onDestroy()"); + } + } + f.mCalled = false; - f.onDestroy(); + f.onDetach(); if (!f.mCalled) { throw new SuperNotCalledException("Fragment " + f - + " did not call through to super.onDestroy()"); + + " did not call through to super.onDetach()"); } + f.mImmediateActivity = null; + f.mActivity = null; + f.mFragmentManager = null; } - - f.mCalled = false; - f.onDetach(); - if (!f.mCalled) { - throw new SuperNotCalledException("Fragment " + f - + " did not call through to super.onDetach()"); - } - f.mImmediateActivity = null; - f.mActivity = null; - f.mFragmentManager = null; } } } @@ -1442,6 +1540,7 @@ final class FragmentManagerImpl extends FragmentManager { } public void dispatchDestroy() { + mDestroyed = true; moveToState(Fragment.INITIALIZING, false); mActivity = null; } |