diff options
| author | Dianne Hackborn <hackbod@google.com> | 2011-01-08 17:25:57 -0800 |
|---|---|---|
| committer | Dianne Hackborn <hackbod@google.com> | 2011-01-08 18:25:30 -0800 |
| commit | 247fe74c934cb3fba85aae7e051a8044f460fb11 (patch) | |
| tree | 91123356af7f3b66c19c974b885eb5d3a94707b5 /core/java/android/content/AsyncTaskLoader.java | |
| parent | f600780bea864c672e01a391b65da65d85045803 (diff) | |
Implement issue # 3255887 could CursorLoader offer...
...to throttle contentobserver-based requeries
Why yes, I guess it could.
This also reworks AsyncTaskLoader to not generate multiple
concurrent tasks if it is getting change notifications before
the last background task is complete.
And removes some of the old APIs that had been deprecated but
need to be gone for final release.
And fixes a few little problems with applying the wrong theme
in system code.
Change-Id: Ic7a665b666d0fb9d348e5f23595532191065884f
Diffstat (limited to 'core/java/android/content/AsyncTaskLoader.java')
| -rw-r--r-- | core/java/android/content/AsyncTaskLoader.java | 153 |
1 files changed, 134 insertions, 19 deletions
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index 01a2912885e7..ec4e578725e9 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -17,8 +17,13 @@ package android.content; import android.os.AsyncTask; +import android.os.Handler; +import android.os.SystemClock; import android.util.Log; +import android.util.TimeUtils; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.concurrent.ExecutionException; /** @@ -30,14 +35,15 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { private static final String TAG = "AsyncTaskLoader"; - final class LoadTask extends AsyncTask<Void, Void, D> { + final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable { - private D result; + D result; + boolean waiting; /* Runs on a worker thread */ @Override protected D doInBackground(Void... params) { - result = AsyncTaskLoader.this.loadInBackground(); + result = AsyncTaskLoader.this.onLoadInBackground(); return result; } @@ -49,38 +55,91 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { @Override protected void onCancelled() { - AsyncTaskLoader.this.onCancelled(result); + AsyncTaskLoader.this.dispatchOnCancelled(this, result); + } + + @Override + public void run() { + waiting = false; + AsyncTaskLoader.this.executePendingTask(); } } volatile LoadTask mTask; + volatile LoadTask mCancellingTask; + + long mUpdateThrottle; + long mLastLoadCompleteTime = -10000; + Handler mHandler; public AsyncTaskLoader(Context context) { super(context); } + /** + * Set amount to throttle updates by. This is the minimum time from + * when the last {@link #onLoadInBackground()} call has completed until + * a new load is scheduled. + * + * @param delayMS Amount of delay, in milliseconds. + */ + public void setUpdateThrottle(long delayMS) { + mUpdateThrottle = delayMS; + if (delayMS != 0) { + mHandler = new Handler(); + } + } + @Override protected void onForceLoad() { super.onForceLoad(); cancelLoad(); mTask = new LoadTask(); - mTask.execute((Void[]) null); + executePendingTask(); } /** * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)} - * for more info. + * for more info. Must be called on the main thread of the process. + * + * <p>Cancelling is not an immediate operation, since the load is performed + * in a background thread. If there is currently a load in progress, this + * method requests that the load be cancelled, and notes this is the case; + * once the background thread has completed its work its remaining state + * will be cleared. If another load request comes in during this time, + * it will be held until the cancelled load is complete. * - * @return <tt>false</tt> if the task could not be canceled, + * @return Returns <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally, or - * because {@link #startLoading()} hasn't been called, and - * <tt>true</tt> otherwise + * because {@link #startLoading()} hasn't been called; returns + * <tt>true</tt> otherwise. */ public boolean cancelLoad() { if (mTask != null) { - boolean cancelled = mTask.cancel(false); - mTask = null; - return cancelled; + if (mCancellingTask != null) { + // There was a pending task already waiting for a previous + // one being canceled; just drop it. + if (mTask.waiting) { + mTask.waiting = false; + mHandler.removeCallbacks(mTask); + } + mTask = null; + return false; + } else if (mTask.waiting) { + // There is a task, but it is waiting for the time it should + // execute. We can just toss it. + mTask.waiting = false; + mHandler.removeCallbacks(mTask); + mTask = null; + return false; + } else { + boolean cancelled = mTask.cancel(false); + if (cancelled) { + mCancellingTask = mTask; + } + mTask = null; + return cancelled; + } } return false; } @@ -92,32 +151,67 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { public void onCancelled(D data) { } + void executePendingTask() { + if (mCancellingTask == null && mTask != null) { + if (mTask.waiting) { + mTask.waiting = false; + mHandler.removeCallbacks(mTask); + } + if (mUpdateThrottle > 0) { + long now = SystemClock.uptimeMillis(); + if (now < (mLastLoadCompleteTime+mUpdateThrottle)) { + // Not yet time to do another load. + mTask.waiting = true; + mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle); + return; + } + } + mTask.execute((Void[]) null); + } + } + + void dispatchOnCancelled(LoadTask task, D data) { + onCancelled(data); + if (mCancellingTask == task) { + mLastLoadCompleteTime = SystemClock.uptimeMillis(); + mCancellingTask = null; + executePendingTask(); + } + } + void dispatchOnLoadComplete(LoadTask task, D data) { if (mTask != task) { - onCancelled(data); + dispatchOnCancelled(task, data); } else { + mLastLoadCompleteTime = SystemClock.uptimeMillis(); mTask = null; deliverResult(data); } } /** + */ + public abstract D loadInBackground(); + + /** * Called on a worker thread to perform the actual load. Implementations should not deliver the - * results directly, but should return them from this method, which will eventually end up - * calling deliverResult on the UI thread. If implementations need to process - * the results on the UI thread they may override deliverResult and do so + * result directly, but should return them from this method, which will eventually end up + * calling {@link #deliverResult} on the UI thread. If implementations need to process + * the results on the UI thread they may override {@link #deliverResult} and do so * there. * - * @return the result of the load + * @return Implementations must return the result of their load operation. */ - public abstract D loadInBackground(); + protected D onLoadInBackground() { + return loadInBackground(); + } /** * Locks the current thread until the loader completes the current load * operation. Returns immediately if there is no load operation running. * Should not be called from the UI thread. * <p> - * Used for testing. + * Use for testing only. <b>Never</b> call this from a UI thread. */ public void waitForLoader() { LoadTask task = mTask; @@ -132,4 +226,25 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { } } } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + super.dump(prefix, fd, writer, args); + if (mTask != null) { + writer.print(prefix); writer.print("mTask="); writer.print(mTask); + writer.print(" waiting="); writer.println(mTask.waiting); + } + if (mCancellingTask != null) { + writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask); + writer.print(" waiting="); writer.println(mCancellingTask.waiting); + } + if (mUpdateThrottle != 0) { + writer.print(prefix); writer.print("mUpdateThrottle="); + TimeUtils.formatDuration(mUpdateThrottle, writer); + writer.print(" mLastLoadCompleteTime="); + TimeUtils.formatDuration(mLastLoadCompleteTime, + SystemClock.uptimeMillis(), writer); + writer.println(); + } + } } |
