diff options
| author | Dianne Hackborn <hackbod@google.com> | 2010-10-25 17:08:56 -0700 |
|---|---|---|
| committer | Dianne Hackborn <hackbod@google.com> | 2010-10-26 11:09:54 -0700 |
| commit | fb3cffeb35368da22f99b85d45039c4e6e471c06 (patch) | |
| tree | 7855a5e97c06009ece8a7fc1fbdddd5f395a3936 /core/java | |
| parent | f0a896b353e01c7093ba2ee0539f1a7cabef6ec6 (diff) | |
| download | frameworks_base-fb3cffeb35368da22f99b85d45039c4e6e471c06.zip frameworks_base-fb3cffeb35368da22f99b85d45039c4e6e471c06.tar.gz frameworks_base-fb3cffeb35368da22f99b85d45039c4e6e471c06.tar.bz2 | |
Fix issue #3130426: Finsky crash in switching from window carousel
Need to note that we no longer have saved state before delivering
results or new intents to an activity.
Also do some work on loaders to prevent apps from making fragment
changes as a result of receiving loader data. This makes apps
consistent crash in a case that they would previously sometimes
crash (if they got the loader data after onPause).
Change-Id: I46e9e46d0aa05d9d7d6a275a2a488a18a20a5747
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/app/Activity.java | 41 | ||||
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 5 | ||||
| -rw-r--r-- | core/java/android/app/Fragment.java | 32 | ||||
| -rw-r--r-- | core/java/android/app/FragmentManager.java | 18 | ||||
| -rw-r--r-- | core/java/android/app/LoaderManager.java | 62 |
5 files changed, 119 insertions, 39 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f08d88d..378a8bd 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -645,11 +645,13 @@ public class Activity extends ContextThemeWrapper Activity mParent; boolean mCalled; boolean mCheckedForLoaderManager; - boolean mStarted; + boolean mLoadersStarted; private boolean mResumed; private boolean mStopped; boolean mFinished; boolean mStartedActivity; + /** true if the activity is going through a transient pause */ + /*package*/ boolean mTemporaryPause = false; /** true if the activity is being destroyed in order to recreate it with a new configuration */ /*package*/ boolean mChangingConfigurations = false; /*package*/ int mConfigChangeFlags; @@ -768,7 +770,7 @@ public class Activity extends ContextThemeWrapper return mLoaderManager; } mCheckedForLoaderManager = true; - mLoaderManager = getLoaderManager(-1, mStarted, true); + mLoaderManager = getLoaderManager(-1, mLoadersStarted, true); return mLoaderManager; } @@ -777,9 +779,13 @@ public class Activity extends ContextThemeWrapper mAllLoaderManagers = new SparseArray<LoaderManagerImpl>(); } LoaderManagerImpl lm = mAllLoaderManagers.get(index); - if (lm == null && create) { - lm = new LoaderManagerImpl(started); - mAllLoaderManagers.put(index, lm); + if (lm == null) { + if (create) { + lm = new LoaderManagerImpl(this, started); + mAllLoaderManagers.put(index, lm); + } + } else { + lm.updateActivity(this); } return lm; } @@ -979,13 +985,16 @@ public class Activity extends ContextThemeWrapper */ protected void onStart() { mCalled = true; - mStarted = true; - if (mLoaderManager != null) { - mLoaderManager.doStart(); - } else if (!mCheckedForLoaderManager) { - mLoaderManager = getLoaderManager(-1, mStarted, false); + + if (!mLoadersStarted) { + mLoadersStarted = true; + if (mLoaderManager != null) { + mLoaderManager.doStart(); + } else if (!mCheckedForLoaderManager) { + mLoaderManager = getLoaderManager(-1, mLoadersStarted, false); + } + mCheckedForLoaderManager = true; } - mCheckedForLoaderManager = true; } /** @@ -4249,7 +4258,7 @@ public class Activity extends ContextThemeWrapper } final void performStart() { - mFragments.mStateSaved = false; + mFragments.noteStateNotSaved(); mCalled = false; mFragments.execPendingActions(); mInstrumentation.callActivityOnStart(this); @@ -4267,7 +4276,7 @@ public class Activity extends ContextThemeWrapper } final void performRestart() { - mFragments.mStateSaved = false; + mFragments.noteStateNotSaved(); synchronized (mManagedCursors) { final int N = mManagedCursors.size(); @@ -4347,8 +4356,8 @@ public class Activity extends ContextThemeWrapper } final void performStop() { - if (mStarted) { - mStarted = false; + if (mLoadersStarted) { + mLoadersStarted = false; if (mLoaderManager != null) { if (!mChangingConfigurations) { mLoaderManager.doStop(); @@ -4407,7 +4416,7 @@ public class Activity extends ContextThemeWrapper if (Config.LOGV) Log.v( TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode + ", resCode=" + resultCode + ", data=" + data); - mFragments.mStateSaved = false; + mFragments.noteStateNotSaved(); if (who == null) { onActivityResult(requestCode, resultCode, data); } else { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f3f7ee7..2abe822 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1757,6 +1757,7 @@ public final class ActivityThread { for (int i=0; i<N; i++) { Intent intent = intents.get(i); intent.setExtrasClassLoader(r.activity.getClassLoader()); + r.activity.mFragments.noteStateNotSaved(); mInstrumentation.callActivityOnNewIntent(r.activity, intent); } } @@ -1767,11 +1768,13 @@ public final class ActivityThread { if (r != null) { final boolean resumed = !r.paused; if (resumed) { + r.activity.mTemporaryPause = true; mInstrumentation.callActivityOnPause(r.activity); } deliverNewIntents(r, intents); if (resumed) { mInstrumentation.callActivityOnResume(r.activity); + r.activity.mTemporaryPause = false; } } } @@ -2594,6 +2597,7 @@ public final class ActivityThread { try { // Now we are idle. r.activity.mCalled = false; + r.activity.mTemporaryPause = true; mInstrumentation.callActivityOnPause(r.activity); if (!r.activity.mCalled) { throw new SuperNotCalledException( @@ -2614,6 +2618,7 @@ public final class ActivityThread { deliverResults(r, res.results); if (resumed) { mInstrumentation.callActivityOnResume(r.activity); + r.activity.mTemporaryPause = false; } } } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 12bf7e5..3ec0912 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -403,7 +403,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener View mView; LoaderManagerImpl mLoaderManager; - boolean mStarted; + boolean mLoadersStarted; boolean mCheckedForLoaderManager; /** @@ -728,7 +728,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener return mLoaderManager; } mCheckedForLoaderManager = true; - mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, true); + mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, true); return mLoaderManager; } @@ -880,13 +880,16 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener */ public void onStart() { mCalled = true; - mStarted = true; - if (!mCheckedForLoaderManager) { - mCheckedForLoaderManager = true; - mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false); - } - if (mLoaderManager != null) { - mLoaderManager.doStart(); + + if (!mLoadersStarted) { + mLoadersStarted = true; + if (!mCheckedForLoaderManager) { + mCheckedForLoaderManager = true; + mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false); + } + if (mLoaderManager != null) { + mLoaderManager.doStart(); + } } } @@ -971,7 +974,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener // + " mLoaderManager=" + mLoaderManager); if (!mCheckedForLoaderManager) { mCheckedForLoaderManager = true; - mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false); + mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false); } if (mLoaderManager != null) { mLoaderManager.doDestroy(); @@ -1182,7 +1185,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener } if (mLoaderManager != null) { writer.print(prefix); writer.print("mLoaderManager="); writer.print(mLoaderManager); - writer.print(" mStarted="); writer.print(mStarted); + writer.print(" mLoadersStarted="); writer.print(mLoadersStarted); writer.print(" mCheckedForLoaderManager="); writer.println(mCheckedForLoaderManager); } @@ -1190,11 +1193,12 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener void performStop() { onStop(); - if (mStarted) { - mStarted = false; + + if (mLoadersStarted) { + mLoadersStarted = false; if (!mCheckedForLoaderManager) { mCheckedForLoaderManager = true; - mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false); + mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false); } if (mLoaderManager != null) { if (mActivity == null || !mActivity.mChangingConfigurations) { diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 37e7253..d9a6171 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -86,6 +86,15 @@ public interface FragmentManager { /** * Start a series of edit operations on the Fragments associated with * this FragmentManager. + * + * <p>Note: A fragment transaction can only be created/committed prior + * to an activity saving its state. If you try to commit a transaction + * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()} + * (and prior to a following {@link Activity#onStart Activity.onStart} + * or {@link Activity#onResume Activity.onResume()}, you will get an error. + * This is because the framework takes care of saving your current fragments + * in the state, and if changes are made after the state is saved then they + * will be lost.</p> */ public FragmentTransaction openTransaction(); @@ -271,6 +280,7 @@ final class FragmentManagerImpl implements FragmentManager { boolean mNeedMenuInvalidate; boolean mStateSaved; + String mNoTransactionsBecause; // Temporary vars for state save and restore. Bundle mStateBundle = null; @@ -843,6 +853,10 @@ final class FragmentManagerImpl implements FragmentManager { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } + if (mNoTransactionsBecause != null) { + throw new IllegalStateException( + "Can not perform this action inside of " + mNoTransactionsBecause); + } synchronized (this) { if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); @@ -1271,6 +1285,10 @@ final class FragmentManagerImpl implements FragmentManager { mActivity = activity; } + public void noteStateNotSaved() { + mStateSaved = false; + } + public void dispatchCreate() { mStateSaved = false; moveToState(Fragment.CREATED, false); diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 28abcaa..4d4ea9a 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -40,7 +40,12 @@ public interface LoaderManager { public Loader<D> onCreateLoader(int id, Bundle args); /** - * Called when a previously created loader has finished its load. + * Called when a previously created loader has finished its load. Note + * that normally an application is <em>not</em> allowed to commit fragment + * transactions while in this call, since it can happen after an + * activity's state is saved. See {@link FragmentManager#openTransaction() + * FragmentManager.openTransaction()} for further discussion on this. + * * @param loader The Loader that has finished. * @param data The data generated by the Loader. */ @@ -102,6 +107,7 @@ class LoaderManagerImpl implements LoaderManager { // previously run loader until the new loader's data is available. final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(); + Activity mActivity; boolean mStarted; boolean mRetaining; boolean mRetainingStarted; @@ -172,12 +178,12 @@ class LoaderManagerImpl implements LoaderManager { stop(); } } - if (mStarted && mData != null && mCallbacks != null) { + if (mStarted && mData != null) { // This loader was retained, and now at the point of // finishing the retain we find we remain started, have // our data, and the owner has a new callback... so // let's deliver the data now. - mCallbacks.onLoadFinished(mLoader, mData); + callOnLoadFinished(mLoader, mData); } } } @@ -219,9 +225,7 @@ class LoaderManagerImpl implements LoaderManager { // Notify of the new data so the app can switch out the old data before // we try to destroy it. mData = data; - if (mCallbacks != null) { - mCallbacks.onLoadFinished(loader, data); - } + callOnLoadFinished(loader, data); if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this); @@ -236,6 +240,23 @@ class LoaderManagerImpl implements LoaderManager { } } + void callOnLoadFinished(Loader<Object> loader, Object data) { + if (mCallbacks != null) { + String lastBecause = null; + if (mActivity != null) { + lastBecause = mActivity.mFragments.mNoTransactionsBecause; + mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished"; + } + try { + mCallbacks.onLoadFinished(loader, data); + } finally { + if (mActivity != null) { + mActivity.mFragments.mNoTransactionsBecause = lastBecause; + } + } + } + } + @Override public String toString() { StringBuilder sb = new StringBuilder(64); @@ -252,10 +273,15 @@ class LoaderManagerImpl implements LoaderManager { } } - LoaderManagerImpl(boolean started) { + LoaderManagerImpl(Activity activity, boolean started) { + mActivity = activity; mStarted = started; } + void updateActivity(Activity activity) { + mActivity = activity; + } + private LoaderInfo createLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callback) { LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); @@ -286,7 +312,7 @@ class LoaderManagerImpl implements LoaderManager { if (info.mData != null && mStarted) { // If the loader has already generated its data, report it now. - info.mCallbacks.onLoadFinished(info.mLoader, info.mData); + info.callOnLoadFinished(info.mLoader, info.mData); } return (Loader<D>)info.mLoader; @@ -348,7 +374,13 @@ class LoaderManagerImpl implements LoaderManager { void doStart() { if (DEBUG) Log.v(TAG, "Starting: " + this); - + if (mStarted) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Called doStart when already started: " + this, e); + return; + } + // Call out to sub classes so they can start their loaders // Let the existing loaders know that we want to be notified when a load is complete for (int i = mLoaders.size()-1; i >= 0; i--) { @@ -359,6 +391,12 @@ class LoaderManagerImpl implements LoaderManager { void doStop() { if (DEBUG) Log.v(TAG, "Stopping: " + this); + if (!mStarted) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Called doStop when not started: " + this, e); + return; + } for (int i = mLoaders.size()-1; i >= 0; i--) { mLoaders.valueAt(i).stop(); @@ -368,6 +406,12 @@ class LoaderManagerImpl implements LoaderManager { void doRetain() { if (DEBUG) Log.v(TAG, "Retaining: " + this); + if (!mStarted) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "Called doRetain when not started: " + this, e); + return; + } mRetaining = true; mStarted = false; |
