summaryrefslogtreecommitdiffstats
path: root/core/java/android/app
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-12-23 13:58:22 -0800
committerDianne Hackborn <hackbod@google.com>2010-12-28 14:01:20 -0800
commitd173fa3b1cb8e4294aba7564c0171894be6c3c24 (patch)
treeaeba6e98ef80d95d291d0fd0874a0122e6220bbd /core/java/android/app
parent7d4b0062f8c7f530d289bfb017f0bced0ffa0f00 (diff)
downloadframeworks_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.java14
-rw-r--r--core/java/android/app/FragmentManager.java159
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;
}