blob: 0b54396ce90a32e21dd492b012d0841adb503b09 [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;
21import android.os.SystemClock;
Dianne Hackborn540f86a2011-01-11 17:52:22 -080022import android.util.Slog;
Dianne Hackborn247fe742011-01-08 17:25:57 -080023import android.util.TimeUtils;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080024
Dianne Hackborn247fe742011-01-08 17:25:57 -080025import java.io.FileDescriptor;
26import java.io.PrintWriter;
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080027import java.util.concurrent.CountDownLatch;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050028
29/**
Dianne Hackborn9567a662011-04-19 18:44:03 -070030 * Abstract Loader that provides an {@link AsyncTask} to do the work. See
31 * {@link Loader} and {@link android.app.LoaderManager} for more details.
32 *
33 * <p>Here is an example implementation of an AsyncTaskLoader subclass that
34 * loads the currently installed applications from the package manager. This
35 * implementation takes care of retrieving the application labels and sorting
36 * its result set from them, monitoring for changes to the installed
37 * applications, and rebuilding the list when a change in configuration requires
38 * this (such as a locale change).
39 *
40 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
41 * loader}
42 *
43 * <p>An example implementation of a fragment that uses the above loader to show
44 * the currently installed applications in a list is below.
45 *
46 * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCustom.java
47 * fragment}
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070048 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050049 * @param <D> the data type to be loaded.
50 */
51public abstract class AsyncTaskLoader<D> extends Loader<D> {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080052 static final String TAG = "AsyncTaskLoader";
53 static final boolean DEBUG = false;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080054
Dianne Hackborn247fe742011-01-08 17:25:57 -080055 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070056
Dianne Hackborn247fe742011-01-08 17:25:57 -080057 D result;
58 boolean waiting;
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070059
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080060 private CountDownLatch done = new CountDownLatch(1);
61
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050062 /* Runs on a worker thread */
63 @Override
64 protected D doInBackground(Void... params) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080065 if (DEBUG) Slog.v(TAG, this + " >>> doInBackground");
Dianne Hackborn247fe742011-01-08 17:25:57 -080066 result = AsyncTaskLoader.this.onLoadInBackground();
Dianne Hackborn540f86a2011-01-11 17:52:22 -080067 if (DEBUG) Slog.v(TAG, this + " <<< doInBackground");
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070068 return result;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050069 }
70
71 /* Runs on the UI thread */
72 @Override
73 protected void onPostExecute(D data) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080074 if (DEBUG) Slog.v(TAG, this + " onPostExecute");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080075 try {
76 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
77 } finally {
78 done.countDown();
79 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050080 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070081
82 @Override
83 protected void onCancelled() {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080084 if (DEBUG) Slog.v(TAG, this + " onCancelled");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080085 try {
86 AsyncTaskLoader.this.dispatchOnCancelled(this, result);
87 } finally {
88 done.countDown();
89 }
Dianne Hackborn247fe742011-01-08 17:25:57 -080090 }
91
92 @Override
93 public void run() {
94 waiting = false;
95 AsyncTaskLoader.this.executePendingTask();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070096 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050097 }
98
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080099 volatile LoadTask mTask;
Dianne Hackborn247fe742011-01-08 17:25:57 -0800100 volatile LoadTask mCancellingTask;
101
102 long mUpdateThrottle;
103 long mLastLoadCompleteTime = -10000;
104 Handler mHandler;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500105
106 public AsyncTaskLoader(Context context) {
107 super(context);
108 }
109
Dianne Hackborn247fe742011-01-08 17:25:57 -0800110 /**
111 * Set amount to throttle updates by. This is the minimum time from
112 * when the last {@link #onLoadInBackground()} call has completed until
113 * a new load is scheduled.
114 *
115 * @param delayMS Amount of delay, in milliseconds.
116 */
117 public void setUpdateThrottle(long delayMS) {
118 mUpdateThrottle = delayMS;
119 if (delayMS != 0) {
120 mHandler = new Handler();
121 }
122 }
123
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500124 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800125 protected void onForceLoad() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800126 super.onForceLoad();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700127 cancelLoad();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500128 mTask = new LoadTask();
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800129 if (DEBUG) Slog.v(TAG, "Preparing load: mTask=" + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800130 executePendingTask();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500131 }
132
133 /**
134 * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
Dianne Hackborn247fe742011-01-08 17:25:57 -0800135 * for more info. Must be called on the main thread of the process.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500136 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800137 * <p>Cancelling is not an immediate operation, since the load is performed
138 * in a background thread. If there is currently a load in progress, this
139 * method requests that the load be cancelled, and notes this is the case;
140 * once the background thread has completed its work its remaining state
141 * will be cleared. If another load request comes in during this time,
142 * it will be held until the cancelled load is complete.
143 *
144 * @return Returns <tt>false</tt> if the task could not be cancelled,
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500145 * typically because it has already completed normally, or
Dianne Hackborn247fe742011-01-08 17:25:57 -0800146 * because {@link #startLoading()} hasn't been called; returns
147 * <tt>true</tt> otherwise.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500148 */
149 public boolean cancelLoad() {
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800150 if (DEBUG) Slog.v(TAG, "cancelLoad: mTask=" + mTask);
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500151 if (mTask != null) {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800152 if (mCancellingTask != null) {
153 // There was a pending task already waiting for a previous
154 // one being canceled; just drop it.
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800155 if (DEBUG) Slog.v(TAG,
156 "cancelLoad: still waiting for cancelled task; dropping next");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800157 if (mTask.waiting) {
158 mTask.waiting = false;
159 mHandler.removeCallbacks(mTask);
160 }
161 mTask = null;
162 return false;
163 } else if (mTask.waiting) {
164 // There is a task, but it is waiting for the time it should
165 // execute. We can just toss it.
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800166 if (DEBUG) Slog.v(TAG, "cancelLoad: task is waiting, dropping it");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800167 mTask.waiting = false;
168 mHandler.removeCallbacks(mTask);
169 mTask = null;
170 return false;
171 } else {
172 boolean cancelled = mTask.cancel(false);
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800173 if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800174 if (cancelled) {
175 mCancellingTask = mTask;
176 }
177 mTask = null;
178 return cancelled;
179 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500180 }
181 return false;
182 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700183
184 /**
185 * Called if the task was canceled before it was completed. Gives the class a chance
186 * to properly dispose of the result.
187 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800188 public void onCanceled(D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800189 }
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800190
Dianne Hackborn247fe742011-01-08 17:25:57 -0800191 void executePendingTask() {
192 if (mCancellingTask == null && mTask != null) {
193 if (mTask.waiting) {
194 mTask.waiting = false;
195 mHandler.removeCallbacks(mTask);
196 }
197 if (mUpdateThrottle > 0) {
198 long now = SystemClock.uptimeMillis();
199 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
200 // Not yet time to do another load.
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800201 if (DEBUG) Slog.v(TAG, "Waiting until "
202 + (mLastLoadCompleteTime+mUpdateThrottle)
203 + " to execute: " + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800204 mTask.waiting = true;
205 mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
206 return;
207 }
208 }
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800209 if (DEBUG) Slog.v(TAG, "Executing: " + mTask);
Dianne Hackborn5d9d03a2011-01-24 13:15:09 -0800210 mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800211 }
212 }
213
214 void dispatchOnCancelled(LoadTask task, D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800215 onCanceled(data);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800216 if (mCancellingTask == task) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800217 if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800218 mLastLoadCompleteTime = SystemClock.uptimeMillis();
219 mCancellingTask = null;
220 executePendingTask();
221 }
222 }
223
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800224 void dispatchOnLoadComplete(LoadTask task, D data) {
225 if (mTask != task) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800226 if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800227 dispatchOnCancelled(task, data);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800228 } else {
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800229 if (isAbandoned()) {
230 // This cursor has been abandoned; just cancel the new data.
231 onCanceled(data);
232 } else {
233 mLastLoadCompleteTime = SystemClock.uptimeMillis();
234 mTask = null;
235 if (DEBUG) Slog.v(TAG, "Delivering result");
236 deliverResult(data);
237 }
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800238 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500239 }
240
241 /**
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500242 */
243 public abstract D loadInBackground();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800244
245 /**
Dianne Hackborn247fe742011-01-08 17:25:57 -0800246 * Called on a worker thread to perform the actual load. Implementations should not deliver the
247 * result directly, but should return them from this method, which will eventually end up
248 * calling {@link #deliverResult} on the UI thread. If implementations need to process
249 * the results on the UI thread they may override {@link #deliverResult} and do so
250 * there.
251 *
252 * @return Implementations must return the result of their load operation.
253 */
254 protected D onLoadInBackground() {
255 return loadInBackground();
256 }
257
258 /**
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800259 * Locks the current thread until the loader completes the current load
260 * operation. Returns immediately if there is no load operation running.
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800261 * Should not be called from the UI thread: calling it from the UI
262 * thread would cause a deadlock.
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800263 * <p>
Dianne Hackborn247fe742011-01-08 17:25:57 -0800264 * Use for testing only. <b>Never</b> call this from a UI thread.
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800265 *
266 * @hide
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800267 */
268 public void waitForLoader() {
269 LoadTask task = mTask;
270 if (task != null) {
271 try {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800272 task.done.await();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800273 } catch (InterruptedException e) {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800274 // Ignore
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800275 }
276 }
277 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800278
279 @Override
280 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
281 super.dump(prefix, fd, writer, args);
282 if (mTask != null) {
283 writer.print(prefix); writer.print("mTask="); writer.print(mTask);
284 writer.print(" waiting="); writer.println(mTask.waiting);
285 }
286 if (mCancellingTask != null) {
287 writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
288 writer.print(" waiting="); writer.println(mCancellingTask.waiting);
289 }
290 if (mUpdateThrottle != 0) {
291 writer.print(prefix); writer.print("mUpdateThrottle=");
292 TimeUtils.formatDuration(mUpdateThrottle, writer);
293 writer.print(" mLastLoadCompleteTime=");
294 TimeUtils.formatDuration(mLastLoadCompleteTime,
295 SystemClock.uptimeMillis(), writer);
296 writer.println();
297 }
298 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500299}