From ca614f78bed7eebf9dbfd77ba5720a0b5eeed816 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 14 Mar 2013 19:10:04 -0700 Subject: [PATCH] Fix a bug where we could lose a loader content change. If AsyncTaskLoader starts a background update due to a content change, and that update is cancelled, we drop the data when it finally arrives and forget that the content changed. If we later come back to the loader, we then end up showing stale data because we don't know that we still need to update due to the old content change. This change adds a couple new APIs to Loader to deal with the time between when you ask for whether there is a content change and finally either commit the data or cancel the update. AsyncTaskLoader is changed to make use of this so that it doesn't lose changes. Change-Id: I3866236b1c22bb9138f2d9f6032b126aeaee2e6e --- api/current.txt | 2 + .../java/android/content/AsyncTaskLoader.java | 2 + core/java/android/content/Loader.java | 42 ++++++++++++++++--- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/api/current.txt b/api/current.txt index be104a02552c0..d03a66727c2e7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6159,6 +6159,7 @@ package android.content { ctor public Loader(android.content.Context); method public void abandon(); method public boolean cancelLoad(); + method public void commitContentChanged(); method public java.lang.String dataToString(D); method public void deliverCancellation(); method public void deliverResult(D); @@ -6179,6 +6180,7 @@ package android.content { method public void registerListener(int, android.content.Loader.OnLoadCompleteListener); method public void registerOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener); method public void reset(); + method public void rollbackContentChanged(); method public final void startLoading(); method public void stopLoading(); method public boolean takeContentChanged(); diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index 188c786881564..612c67f01cc05 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -231,6 +231,7 @@ public abstract class AsyncTaskLoader extends Loader { onCanceled(data); if (mCancellingTask == task) { if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!"); + rollbackContentChanged(); mLastLoadCompleteTime = SystemClock.uptimeMillis(); mCancellingTask = null; if (DEBUG) Slog.v(TAG, "Delivering cancellation"); @@ -248,6 +249,7 @@ public abstract class AsyncTaskLoader extends Loader { // This cursor has been abandoned; just cancel the new data. onCanceled(data); } else { + commitContentChanged(); mLastLoadCompleteTime = SystemClock.uptimeMillis(); mTask = null; if (DEBUG) Slog.v(TAG, "Delivering result"); diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index 30524148d6896..911e49ca604ea 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -58,6 +58,7 @@ public class Loader { boolean mAbandoned = false; boolean mReset = true; boolean mContentChanged = false; + boolean mProcessingChange = false; /** * An implementation of a ContentObserver that takes care of connecting @@ -439,6 +440,7 @@ public class Loader { mStarted = false; mAbandoned = false; mContentChanged = false; + mProcessingChange = false; } /** @@ -458,9 +460,34 @@ public class Loader { public boolean takeContentChanged() { boolean res = mContentChanged; mContentChanged = false; + mProcessingChange |= res; return res; } - + + /** + * Commit that you have actually fully processed a content change that + * was returned by {@link #takeContentChanged}. This is for use with + * {@link #rollbackContentChanged()} to handle situations where a load + * is cancelled. Call this when you have completely processed a load + * without it being cancelled. + */ + public void commitContentChanged() { + mProcessingChange = false; + } + + /** + * Report that you have abandoned the processing of a content change that + * was returned by {@link #takeContentChanged()} and would like to rollback + * to the state where there is again a pending content change. This is + * to handle the case where a data load due to a content change has been + * canceled before its data was delivered back to the loader. + */ + public void rollbackContentChanged() { + if (mProcessingChange) { + mContentChanged = true; + } + } + /** * Called when {@link ForceLoadContentObserver} detects a change. The * default implementation checks to see if the loader is currently started; @@ -512,9 +539,14 @@ public class Loader { public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { writer.print(prefix); writer.print("mId="); writer.print(mId); writer.print(" mListener="); writer.println(mListener); - writer.print(prefix); writer.print("mStarted="); writer.print(mStarted); - writer.print(" mContentChanged="); writer.print(mContentChanged); - writer.print(" mAbandoned="); writer.print(mAbandoned); - writer.print(" mReset="); writer.println(mReset); + if (mStarted || mContentChanged || mProcessingChange) { + writer.print(prefix); writer.print("mStarted="); writer.print(mStarted); + writer.print(" mContentChanged="); writer.print(mContentChanged); + writer.print(" mProcessingChange="); writer.println(mProcessingChange); + } + if (mAbandoned || mReset) { + writer.print(prefix); writer.print("mAbandoned="); writer.print(mAbandoned); + writer.print(" mReset="); writer.println(mReset); + } } } \ No newline at end of file