blob: 6e9f09cbef0e039975ea6804ec7b8f45b29278b5 [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.
Ian Lake0a1feb82017-11-13 10:26:46 -080052 *
53 * @deprecated Use {@link android.support.v4.content.AsyncTaskLoader}
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050054 */
Ian Lake0a1feb82017-11-13 10:26:46 -080055@Deprecated
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050056public abstract class AsyncTaskLoader<D> extends Loader<D> {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080057 static final String TAG = "AsyncTaskLoader";
58 static final boolean DEBUG = false;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080059
Dianne Hackborn247fe742011-01-08 17:25:57 -080060 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
Jeff Brownb19a71a2012-01-31 11:48:39 -080061 private final CountDownLatch mDone = new CountDownLatch(1);
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070062
Jeff Brownb19a71a2012-01-31 11:48:39 -080063 // Set to true to indicate that the task has been posted to a handler for
64 // execution at a later time. Used to throttle updates.
Dianne Hackborn247fe742011-01-08 17:25:57 -080065 boolean waiting;
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070066
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050067 /* Runs on a worker thread */
68 @Override
69 protected D doInBackground(Void... params) {
Jeff Brownc64ff372013-10-09 18:50:56 -070070 if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
Jeff Brownb19a71a2012-01-31 11:48:39 -080071 try {
72 D data = AsyncTaskLoader.this.onLoadInBackground();
Jeff Brownc64ff372013-10-09 18:50:56 -070073 if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
Jeff Brownb19a71a2012-01-31 11:48:39 -080074 return data;
75 } catch (OperationCanceledException ex) {
76 if (!isCancelled()) {
77 // onLoadInBackground threw a canceled exception spuriously.
78 // This is problematic because it means that the LoaderManager did not
79 // cancel the Loader itself and still expects to receive a result.
80 // Additionally, the Loader's own state will not have been updated to
81 // reflect the fact that the task was being canceled.
82 // So we treat this case as an unhandled exception.
83 throw ex;
84 }
Jeff Brownc64ff372013-10-09 18:50:56 -070085 if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex);
Jeff Brownb19a71a2012-01-31 11:48:39 -080086 return null;
87 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050088 }
89
90 /* Runs on the UI thread */
91 @Override
92 protected void onPostExecute(D data) {
Jeff Brownc64ff372013-10-09 18:50:56 -070093 if (DEBUG) Log.v(TAG, this + " onPostExecute");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080094 try {
95 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
96 } finally {
Jeff Brownb19a71a2012-01-31 11:48:39 -080097 mDone.countDown();
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080098 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050099 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700100
Jeff Brownb19a71a2012-01-31 11:48:39 -0800101 /* Runs on the UI thread */
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700102 @Override
Jeff Brownb19a71a2012-01-31 11:48:39 -0800103 protected void onCancelled(D data) {
Jeff Brownc64ff372013-10-09 18:50:56 -0700104 if (DEBUG) Log.v(TAG, this + " onCancelled");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800105 try {
Jeff Brownb19a71a2012-01-31 11:48:39 -0800106 AsyncTaskLoader.this.dispatchOnCancelled(this, data);
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800107 } finally {
Jeff Brownb19a71a2012-01-31 11:48:39 -0800108 mDone.countDown();
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800109 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800110 }
111
Jeff Brownb19a71a2012-01-31 11:48:39 -0800112 /* Runs on the UI thread, when the waiting task is posted to a handler.
113 * This method is only executed when task execution was deferred (waiting was true). */
Dianne Hackborn247fe742011-01-08 17:25:57 -0800114 @Override
115 public void run() {
116 waiting = false;
117 AsyncTaskLoader.this.executePendingTask();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700118 }
Jeff Brownb19a71a2012-01-31 11:48:39 -0800119
120 /* Used for testing purposes to wait for the task to complete. */
121 public void waitForLoader() {
122 try {
123 mDone.await();
124 } catch (InterruptedException e) {
125 // Ignore
126 }
127 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500128 }
129
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700130 private final Executor mExecutor;
131
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800132 volatile LoadTask mTask;
Dianne Hackborn247fe742011-01-08 17:25:57 -0800133 volatile LoadTask mCancellingTask;
134
135 long mUpdateThrottle;
136 long mLastLoadCompleteTime = -10000;
137 Handler mHandler;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500138
139 public AsyncTaskLoader(Context context) {
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700140 this(context, AsyncTask.THREAD_POOL_EXECUTOR);
141 }
142
143 /** {@hide} */
144 public AsyncTaskLoader(Context context, Executor executor) {
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500145 super(context);
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700146 mExecutor = executor;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500147 }
148
Dianne Hackborn247fe742011-01-08 17:25:57 -0800149 /**
150 * Set amount to throttle updates by. This is the minimum time from
Jeff Brownb19a71a2012-01-31 11:48:39 -0800151 * when the last {@link #loadInBackground()} call has completed until
Dianne Hackborn247fe742011-01-08 17:25:57 -0800152 * a new load is scheduled.
153 *
154 * @param delayMS Amount of delay, in milliseconds.
155 */
156 public void setUpdateThrottle(long delayMS) {
157 mUpdateThrottle = delayMS;
158 if (delayMS != 0) {
159 mHandler = new Handler();
160 }
161 }
162
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500163 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800164 protected void onForceLoad() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800165 super.onForceLoad();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700166 cancelLoad();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500167 mTask = new LoadTask();
Jeff Brownc64ff372013-10-09 18:50:56 -0700168 if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800169 executePendingTask();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500170 }
171
Jeff Brownb19a71a2012-01-31 11:48:39 -0800172 @Override
173 protected boolean onCancelLoad() {
Jeff Brownc64ff372013-10-09 18:50:56 -0700174 if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500175 if (mTask != null) {
George Mount899ca322017-03-08 14:53:43 -0800176 if (!mStarted) {
177 mContentChanged = true;
178 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800179 if (mCancellingTask != null) {
180 // There was a pending task already waiting for a previous
181 // one being canceled; just drop it.
Jeff Brownc64ff372013-10-09 18:50:56 -0700182 if (DEBUG) Log.v(TAG,
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800183 "cancelLoad: still waiting for cancelled task; dropping next");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800184 if (mTask.waiting) {
185 mTask.waiting = false;
186 mHandler.removeCallbacks(mTask);
187 }
188 mTask = null;
189 return false;
190 } else if (mTask.waiting) {
191 // There is a task, but it is waiting for the time it should
192 // execute. We can just toss it.
Jeff Brownc64ff372013-10-09 18:50:56 -0700193 if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800194 mTask.waiting = false;
195 mHandler.removeCallbacks(mTask);
196 mTask = null;
197 return false;
198 } else {
199 boolean cancelled = mTask.cancel(false);
Jeff Brownc64ff372013-10-09 18:50:56 -0700200 if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800201 if (cancelled) {
202 mCancellingTask = mTask;
Jeff Brownb19a71a2012-01-31 11:48:39 -0800203 cancelLoadInBackground();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800204 }
205 mTask = null;
206 return cancelled;
207 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500208 }
209 return false;
210 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700211
212 /**
213 * Called if the task was canceled before it was completed. Gives the class a chance
Jeff Brownb19a71a2012-01-31 11:48:39 -0800214 * to clean up post-cancellation and to properly dispose of the result.
215 *
216 * @param data The value that was returned by {@link #loadInBackground}, or null
217 * if the task threw {@link OperationCanceledException}.
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700218 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800219 public void onCanceled(D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800220 }
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800221
Dianne Hackborn247fe742011-01-08 17:25:57 -0800222 void executePendingTask() {
223 if (mCancellingTask == null && mTask != null) {
224 if (mTask.waiting) {
225 mTask.waiting = false;
226 mHandler.removeCallbacks(mTask);
227 }
228 if (mUpdateThrottle > 0) {
229 long now = SystemClock.uptimeMillis();
230 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
231 // Not yet time to do another load.
Jeff Brownc64ff372013-10-09 18:50:56 -0700232 if (DEBUG) Log.v(TAG, "Waiting until "
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800233 + (mLastLoadCompleteTime+mUpdateThrottle)
234 + " to execute: " + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800235 mTask.waiting = true;
236 mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
237 return;
238 }
239 }
Jeff Brownc64ff372013-10-09 18:50:56 -0700240 if (DEBUG) Log.v(TAG, "Executing: " + mTask);
Jeff Sharkeyd01571e2013-10-01 17:57:41 -0700241 mTask.executeOnExecutor(mExecutor, (Void[]) null);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800242 }
243 }
244
245 void dispatchOnCancelled(LoadTask task, D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800246 onCanceled(data);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800247 if (mCancellingTask == task) {
Jeff Brownc64ff372013-10-09 18:50:56 -0700248 if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
Dianne Hackbornca614f72013-03-14 19:10:04 -0700249 rollbackContentChanged();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800250 mLastLoadCompleteTime = SystemClock.uptimeMillis();
251 mCancellingTask = null;
Jeff Brownc64ff372013-10-09 18:50:56 -0700252 if (DEBUG) Log.v(TAG, "Delivering cancellation");
Jeff Brownb19a71a2012-01-31 11:48:39 -0800253 deliverCancellation();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800254 executePendingTask();
255 }
256 }
257
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800258 void dispatchOnLoadComplete(LoadTask task, D data) {
259 if (mTask != task) {
Jeff Brownc64ff372013-10-09 18:50:56 -0700260 if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800261 dispatchOnCancelled(task, data);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800262 } else {
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800263 if (isAbandoned()) {
264 // This cursor has been abandoned; just cancel the new data.
265 onCanceled(data);
266 } else {
Dianne Hackbornca614f72013-03-14 19:10:04 -0700267 commitContentChanged();
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800268 mLastLoadCompleteTime = SystemClock.uptimeMillis();
269 mTask = null;
Jeff Brownc64ff372013-10-09 18:50:56 -0700270 if (DEBUG) Log.v(TAG, "Delivering result");
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800271 deliverResult(data);
272 }
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800273 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500274 }
275
276 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800277 * Called on a worker thread to perform the actual load and to return
278 * the result of the load operation.
279 *
280 * Implementations should not deliver the result directly, but should return them
281 * from this method, which will eventually end up calling {@link #deliverResult} on
282 * the UI thread. If implementations need to process the results on the UI thread
283 * they may override {@link #deliverResult} and do so there.
284 *
285 * To support cancellation, this method should periodically check the value of
286 * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
287 * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
288 * directly instead of polling {@link #isLoadInBackgroundCanceled}.
289 *
290 * When the load is canceled, this method may either return normally or throw
291 * {@link OperationCanceledException}. In either case, the {@link Loader} will
292 * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
293 * result object, if any.
294 *
295 * @return The result of the load operation.
296 *
297 * @throws OperationCanceledException if the load is canceled during execution.
298 *
299 * @see #isLoadInBackgroundCanceled
300 * @see #cancelLoadInBackground
301 * @see #onCanceled
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500302 */
303 public abstract D loadInBackground();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800304
305 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800306 * Calls {@link #loadInBackground()}.
Dianne Hackborn247fe742011-01-08 17:25:57 -0800307 *
Jeff Brownb19a71a2012-01-31 11:48:39 -0800308 * This method is reserved for use by the loader framework.
309 * Subclasses should override {@link #loadInBackground} instead of this method.
310 *
311 * @return The result of the load operation.
312 *
313 * @throws OperationCanceledException if the load is canceled during execution.
314 *
315 * @see #loadInBackground
Dianne Hackborn247fe742011-01-08 17:25:57 -0800316 */
317 protected D onLoadInBackground() {
318 return loadInBackground();
319 }
320
321 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800322 * Called on the main thread to abort a load in progress.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800323 *
Jeff Brownb19a71a2012-01-31 11:48:39 -0800324 * Override this method to abort the current invocation of {@link #loadInBackground}
325 * that is running in the background on a worker thread.
326 *
327 * This method should do nothing if {@link #loadInBackground} has not started
328 * running or if it has already finished.
329 *
330 * @see #loadInBackground
Jeff Brown75ea64f2012-01-25 19:37:13 -0800331 */
Jeff Brownb19a71a2012-01-31 11:48:39 -0800332 public void cancelLoadInBackground() {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800333 }
334
335 /**
Jeff Brownb19a71a2012-01-31 11:48:39 -0800336 * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
Jeff Brown75ea64f2012-01-25 19:37:13 -0800337 *
Jeff Brownb19a71a2012-01-31 11:48:39 -0800338 * @return True if the current invocation of {@link #loadInBackground} is being canceled.
339 *
340 * @see #loadInBackground
Jeff Brown75ea64f2012-01-25 19:37:13 -0800341 */
Jeff Brownb19a71a2012-01-31 11:48:39 -0800342 public boolean isLoadInBackgroundCanceled() {
Jeff Brown75ea64f2012-01-25 19:37:13 -0800343 return mCancellingTask != null;
344 }
345
346 /**
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800347 * Locks the current thread until the loader completes the current load
348 * operation. Returns immediately if there is no load operation running.
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800349 * Should not be called from the UI thread: calling it from the UI
350 * thread would cause a deadlock.
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800351 * <p>
Dianne Hackborn247fe742011-01-08 17:25:57 -0800352 * Use for testing only. <b>Never</b> call this from a UI thread.
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800353 *
354 * @hide
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800355 */
356 public void waitForLoader() {
357 LoadTask task = mTask;
358 if (task != null) {
Jeff Brownb19a71a2012-01-31 11:48:39 -0800359 task.waitForLoader();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800360 }
361 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800362
363 @Override
364 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
365 super.dump(prefix, fd, writer, args);
366 if (mTask != null) {
367 writer.print(prefix); writer.print("mTask="); writer.print(mTask);
368 writer.print(" waiting="); writer.println(mTask.waiting);
369 }
370 if (mCancellingTask != null) {
371 writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
372 writer.print(" waiting="); writer.println(mCancellingTask.waiting);
373 }
374 if (mUpdateThrottle != 0) {
375 writer.print(prefix); writer.print("mUpdateThrottle=");
376 TimeUtils.formatDuration(mUpdateThrottle, writer);
377 writer.print(" mLastLoadCompleteTime=");
378 TimeUtils.formatDuration(mLastLoadCompleteTime,
379 SystemClock.uptimeMillis(), writer);
380 writer.println();
381 }
382 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500383}