blob: 7241e0d1f2b4be66e529857db62db75ff225dd61 [file] [log] [blame]
Jeff Hamilton9911b7f2010-05-15 02:20:31 -05001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content;
18
19import android.os.AsyncTask;
Dianne Hackborn247fe742011-01-08 17:25:57 -080020import android.os.Handler;
Jeff Browna7771df2012-05-07 20:06:46 -070021import android.os.OperationCanceledException;
Dianne Hackborn247fe742011-01-08 17:25:57 -080022import android.os.SystemClock;
Jeff Brownc64ff372013-10-09 18:50:56 -070023import android.util.Log;
Dianne Hackborn247fe742011-01-08 17:25:57 -080024import android.util.TimeUtils;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080025
Dianne Hackborn247fe742011-01-08 17:25:57 -080026import java.io.FileDescriptor;
27import java.io.PrintWriter;
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080028import java.util.concurrent.CountDownLatch;
Jeff Sharkeyd01571e2013-10-01 17:57:41 -070029import java.util.concurrent.Executor;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050030
31/**
Dianne Hackborn9567a662011-04-19 18:44:03 -070032 * Abstract Loader that provides an {@link AsyncTask} to do the work. See
33 * {@link Loader} and {@link android.app.LoaderManager} for more details.
34 *
35 * <p>Here is an example implementation of an AsyncTaskLoader subclass that
36 * loads the currently installed applications from the package manager. This
37 * implementation takes care of retrieving the application labels and sorting
38 * its result set from them, monitoring for changes to the installed
39 * applications, and rebuilding the list when a change in configuration requires
40 * this (such as a locale change).
41 *
42 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
43 * loader}
44 *
45 * <p>An example implementation of a fragment that uses the above loader to show
46 * the currently installed applications in a list is below.
47 *
48 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
49 * fragment}
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070050 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050051 * @param <D> the data type to be loaded.
52 */
53public abstract class AsyncTaskLoader<D> extends Loader<D> {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080054 static final String TAG = "AsyncTaskLoader";
55 static final boolean DEBUG = false;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080056
Dianne Hackborn247fe742011-01-08 17:25:57 -080057 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
Jeff Brownb19a71a2012-01-31 11:48:39 -080058 private final CountDownLatch mDone = new CountDownLatch(1);
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070059
Jeff Brownb19a71a2012-01-31 11:48:39 -080060 // Set to true to indicate that the task has been posted to a handler for
61 // execution at a later time. Used to throttle updates.
Dianne Hackborn247fe742011-01-08 17:25:57 -080062 boolean waiting;
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070063
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050064 /* Runs on a worker thread */
65 @Override
66 protected D doInBackground(Void... params) {
Jeff Brownc64ff372013-10-09 18:50:56 -070067 if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
Jeff Brownb19a71a2012-01-31 11:48:39 -080068 try {
69 D data = AsyncTaskLoader.this.onLoadInBackground();
Jeff Brownc64ff372013-10-09 18:50:56 -070070 if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
Jeff Brownb19a71a2012-01-31 11:48:39 -080071 return data;
72 } catch (OperationCanceledException ex) {
73 if (!isCancelled()) {
74 // onLoadInBackground threw a canceled exception spuriously.
75 // This is problematic because it means that the LoaderManager did not
76 // cancel the Loader itself and still expects to receive a result.
77 // Additionally, the Loader's own state will not have been updated to
78 // reflect the fact that the task was being canceled.
79 // So we treat this case as an unhandled exception.
80 throw ex;
81 }
Jeff Brownc64ff372013-10-09 18:50:56 -070082 if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex);
Jeff Brownb19a71a2012-01-31 11:48:39 -080083 return null;
84 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050085 }
86
87 /* Runs on the UI thread */
88 @Override
89 protected void onPostExecute(D data) {
Jeff Brownc64ff372013-10-09 18:50:56 -070090 if (DEBUG) Log.v(TAG, this + " onPostExecute");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080091 try {
92 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
93 } finally {
Jeff Brownb19a71a2012-01-31 11:48:39 -080094 mDone.countDown();
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080095 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050096 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070097
Jeff Brownb19a71a2012-01-31 11:48:39 -080098 /* Runs on the UI thread */
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070099 @Override
Jeff Brownb19a71a2012-01-31 11:48:39 -0800100 protected void onCancelled(D data) {
Jeff Brownc64ff372013-10-09 18:50:56 -0700101 if (DEBUG) Log.v(TAG, this + " onCancelled");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800102 try {
Jeff Brownb19a71a2012-01-31 11:48:39 -0800103 AsyncTaskLoader.this.dispatchOnCancelled(this, data);
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800104 } finally {
Jeff Brownb19a71a2012-01-31 11:48:39 -0800105 mDone.countDown();
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800106 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800107 }
108
Jeff Brownb19a71a2012-01-31 11:48:39 -0800109 /* Runs on the UI thread, when the waiting task is posted to a handler.
110 * This method is only executed when task execution was deferred (waiting was true). */
Dianne Hackborn247fe742011-01-08 17:25:57 -0800111 @Override
112 public void run() {
113 waiting = false;
114 AsyncTaskLoader.this.executePendingTask();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700115 }
Jeff Brownb19a71a2012-01-31 11:48:39 -0800116
117 /* Used for testing purposes to wait for the task to complete. */
118 public void waitForLoader() {
119 try {
120 mDone.await();
121 } catch (InterruptedException e) {
122 // Ignore
123 }
124 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500125 }
126
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700127 private final Executor mExecutor;
128
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800129 volatile LoadTask mTask;
Dianne Hackborn247fe742011-01-08 17:25:57 -0800130 volatile LoadTask mCancellingTask;
131
132 long mUpdateThrottle;
133 long mLastLoadCompleteTime = -10000;
134 Handler mHandler;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500135
136 public AsyncTaskLoader(Context context) {
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700137 this(context, AsyncTask.THREAD_POOL_EXECUTOR);
138 }
139
140 /** {@hide} */
141 public AsyncTaskLoader(Context context, Executor executor) {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500142 super(context);
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700143 mExecutor = executor;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500144 }
145
Dianne Hackborn247fe742011-01-08 17:25:57 -0800146 /**
147 * Set amount to throttle updates by. This is the minimum time from
Jeff Brownb19a71a2012-01-31 11:48:39 -0800148 * when the last {@link #loadInBackground()} call has completed until
Dianne Hackborn247fe742011-01-08 17:25:57 -0800149 * a new load is scheduled.
150 *
151 * @param delayMS Amount of delay, in milliseconds.
152 */
153 public void setUpdateThrottle(long delayMS) {
154 mUpdateThrottle = delayMS;
155 if (delayMS != 0) {
156 mHandler = new Handler();
157 }
158 }
159
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500160 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800161 protected void onForceLoad() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800162 super.onForceLoad();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700163 cancelLoad();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500164 mTask = new LoadTask();
Jeff Brownc64ff372013-10-09 18:50:56 -0700165 if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800166 executePendingTask();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500167 }
168
Jeff Brownb19a71a2012-01-31 11:48:39 -0800169 @Override
170 protected boolean onCancelLoad() {
Jeff Brownc64ff372013-10-09 18:50:56 -0700171 if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500172 if (mTask != null) {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800173 if (mCancellingTask != null) {
174 // There was a pending task already waiting for a previous
175 // one being canceled; just drop it.
Jeff Brownc64ff372013-10-09 18:50:56 -0700176 if (DEBUG) Log.v(TAG,
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800177 "cancelLoad: still waiting for cancelled task; dropping next");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800178 if (mTask.waiting) {
179 mTask.waiting = false;
180 mHandler.removeCallbacks(mTask);
181 }
182 mTask = null;
183 return false;
184 } else if (mTask.waiting) {
185 // There is a task, but it is waiting for the time it should
186 // execute. We can just toss it.
Jeff Brownc64ff372013-10-09 18:50:56 -0700187 if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800188 mTask.waiting = false;
189 mHandler.removeCallbacks(mTask);
190 mTask = null;
191 return false;
192 } else {
193 boolean cancelled = mTask.cancel(false);
Jeff Brownc64ff372013-10-09 18:50:56 -0700194 if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800195 if (cancelled) {
196 mCancellingTask = mTask;
Jeff Brownb19a71a2012-01-31 11:48:39 -0800197 cancelLoadInBackground();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800198 }
199 mTask = null;
200 return cancelled;
201 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500202 }
203 return false;
204 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700205
206 /**
207 * Called if the task was canceled before it was completed. Gives the class a chance
Jeff Brownb19a71a2012-01-31 11:48:39 -0800208 * to clean up post-cancellation and to properly dispose of the result.
209 *
210 * @param data The value that was returned by {@link #loadInBackground}, or null
211 * if the task threw {@link OperationCanceledException}.
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700212 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800213 public void onCanceled(D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800214 }
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800215
Dianne Hackborn247fe742011-01-08 17:25:57 -0800216 void executePendingTask() {
217 if (mCancellingTask == null && mTask != null) {
218 if (mTask.waiting) {
219 mTask.waiting = false;
220 mHandler.removeCallbacks(mTask);
221 }
222 if (mUpdateThrottle > 0) {
223 long now = SystemClock.uptimeMillis();
224 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
225 // Not yet time to do another load.
Jeff Brownc64ff372013-10-09 18:50:56 -0700226 if (DEBUG) Log.v(TAG, "Waiting until "
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800227 + (mLastLoadCompleteTime+mUpdateThrottle)
228 + " to execute: " + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800229 mTask.waiting = true;
230 mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
231 return;
232 }
233 }
Jeff Brownc64ff372013-10-09 18:50:56 -0700234 if (DEBUG) Log.v(TAG, "Executing: " + mTask);
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700235 mTask.executeOnExecutor(mExecutor, (Void[]) null);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800236 }
237 }
238
239 void dispatchOnCancelled(LoadTask task, D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800240 onCanceled(data);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800241 if (mCancellingTask == task) {
Jeff Brownc64ff372013-10-09 18:50:56 -0700242 if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
Dianne Hackbornca614f72013-03-14 19:10:04 -0700243 rollbackContentChanged();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800244 mLastLoadCompleteTime = SystemClock.uptimeMillis();
245 mCancellingTask = null;
Jeff Brownc64ff372013-10-09 18:50:56 -0700246 if (DEBUG) Log.v(TAG, "Delivering cancellation");
Jeff Brownb19a71a2012-01-31 11:48:39 -0800247 deliverCancellation();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800248 executePendingTask();
249 }
250 }
251
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800252 void dispatchOnLoadComplete(LoadTask task, D data) {
253 if (mTask != task) {
Jeff Brownc64ff372013-10-09 18:50:56 -0700254 if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800255 dispatchOnCancelled(task, data);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800256 } else {
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800257 if (isAbandoned()) {
258 // This cursor has been abandoned; just cancel the new data.
259 onCanceled(data);
260 } else {
Dianne Hackbornca614f72013-03-14 19:10:04 -0700261 commitContentChanged();
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800262 mLastLoadCompleteTime = SystemClock.uptimeMillis();
263 mTask = null;
Jeff Brownc64ff372013-10-09 18:50:56 -0700264 if (DEBUG) Log.v(TAG, "Delivering result");
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800265 deliverResult(data);
266 }
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800267 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500268 }
269
270 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800271 * Called on a worker thread to perform the actual load and to return
272 * the result of the load operation.
273 *
274 * Implementations should not deliver the result directly, but should return them
275 * from this method, which will eventually end up calling {@link #deliverResult} on
276 * the UI thread. If implementations need to process the results on the UI thread
277 * they may override {@link #deliverResult} and do so there.
278 *
279 * To support cancellation, this method should periodically check the value of
280 * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
281 * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
282 * directly instead of polling {@link #isLoadInBackgroundCanceled}.
283 *
284 * When the load is canceled, this method may either return normally or throw
285 * {@link OperationCanceledException}. In either case, the {@link Loader} will
286 * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
287 * result object, if any.
288 *
289 * @return The result of the load operation.
290 *
291 * @throws OperationCanceledException if the load is canceled during execution.
292 *
293 * @see #isLoadInBackgroundCanceled
294 * @see #cancelLoadInBackground
295 * @see #onCanceled
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500296 */
297 public abstract D loadInBackground();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800298
299 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800300 * Calls {@link #loadInBackground()}.
Dianne Hackborn247fe742011-01-08 17:25:57 -0800301 *
Jeff Brownb19a71a2012-01-31 11:48:39 -0800302 * This method is reserved for use by the loader framework.
303 * Subclasses should override {@link #loadInBackground} instead of this method.
304 *
305 * @return The result of the load operation.
306 *
307 * @throws OperationCanceledException if the load is canceled during execution.
308 *
309 * @see #loadInBackground
Dianne Hackborn247fe742011-01-08 17:25:57 -0800310 */
311 protected D onLoadInBackground() {
312 return loadInBackground();
313 }
314
315 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800316 * Called on the main thread to abort a load in progress.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800317 *
Jeff Brownb19a71a2012-01-31 11:48:39 -0800318 * Override this method to abort the current invocation of {@link #loadInBackground}
319 * that is running in the background on a worker thread.
320 *
321 * This method should do nothing if {@link #loadInBackground} has not started
322 * running or if it has already finished.
323 *
324 * @see #loadInBackground
Jeff Brown75ea64f2012-01-25 19:37:13 -0800325 */
Jeff Brownb19a71a2012-01-31 11:48:39 -0800326 public void cancelLoadInBackground() {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800327 }
328
329 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800330 * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800331 *
Jeff Brownb19a71a2012-01-31 11:48:39 -0800332 * @return True if the current invocation of {@link #loadInBackground} is being canceled.
333 *
334 * @see #loadInBackground
Jeff Brown75ea64f2012-01-25 19:37:13 -0800335 */
Jeff Brownb19a71a2012-01-31 11:48:39 -0800336 public boolean isLoadInBackgroundCanceled() {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800337 return mCancellingTask != null;
338 }
339
340 /**
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800341 * Locks the current thread until the loader completes the current load
342 * operation. Returns immediately if there is no load operation running.
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800343 * Should not be called from the UI thread: calling it from the UI
344 * thread would cause a deadlock.
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800345 * <p>
Dianne Hackborn247fe742011-01-08 17:25:57 -0800346 * Use for testing only. <b>Never</b> call this from a UI thread.
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800347 *
348 * @hide
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800349 */
350 public void waitForLoader() {
351 LoadTask task = mTask;
352 if (task != null) {
Jeff Brownb19a71a2012-01-31 11:48:39 -0800353 task.waitForLoader();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800354 }
355 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800356
357 @Override
358 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
359 super.dump(prefix, fd, writer, args);
360 if (mTask != null) {
361 writer.print(prefix); writer.print("mTask="); writer.print(mTask);
362 writer.print(" waiting="); writer.println(mTask.waiting);
363 }
364 if (mCancellingTask != null) {
365 writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
366 writer.print(" waiting="); writer.println(mCancellingTask.waiting);
367 }
368 if (mUpdateThrottle != 0) {
369 writer.print(prefix); writer.print("mUpdateThrottle=");
370 TimeUtils.formatDuration(mUpdateThrottle, writer);
371 writer.print(" mLastLoadCompleteTime=");
372 TimeUtils.formatDuration(mLastLoadCompleteTime,
373 SystemClock.uptimeMillis(), writer);
374 writer.println();
375 }
376 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500377}