blob: 383cb6b24163d532614bcffcde4a3ae184fbf98f [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/**
30 * Abstract Loader that provides an {@link AsyncTask} to do the work.
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070031 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050032 * @param <D> the data type to be loaded.
33 */
34public abstract class AsyncTaskLoader<D> extends Loader<D> {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080035 static final String TAG = "AsyncTaskLoader";
36 static final boolean DEBUG = false;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080037
Dianne Hackborn247fe742011-01-08 17:25:57 -080038 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070039
Dianne Hackborn247fe742011-01-08 17:25:57 -080040 D result;
41 boolean waiting;
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070042
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080043 private CountDownLatch done = new CountDownLatch(1);
44
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050045 /* Runs on a worker thread */
46 @Override
47 protected D doInBackground(Void... params) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080048 if (DEBUG) Slog.v(TAG, this + " >>> doInBackground");
Dianne Hackborn247fe742011-01-08 17:25:57 -080049 result = AsyncTaskLoader.this.onLoadInBackground();
Dianne Hackborn540f86a2011-01-11 17:52:22 -080050 if (DEBUG) Slog.v(TAG, this + " <<< doInBackground");
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070051 return result;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050052 }
53
54 /* Runs on the UI thread */
55 @Override
56 protected void onPostExecute(D data) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080057 if (DEBUG) Slog.v(TAG, this + " onPostExecute");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080058 try {
59 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
60 } finally {
61 done.countDown();
62 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050063 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070064
65 @Override
66 protected void onCancelled() {
Dianne Hackborn540f86a2011-01-11 17:52:22 -080067 if (DEBUG) Slog.v(TAG, this + " onCancelled");
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080068 try {
69 AsyncTaskLoader.this.dispatchOnCancelled(this, result);
70 } finally {
71 done.countDown();
72 }
Dianne Hackborn247fe742011-01-08 17:25:57 -080073 }
74
75 @Override
76 public void run() {
77 waiting = false;
78 AsyncTaskLoader.this.executePendingTask();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070079 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050080 }
81
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080082 volatile LoadTask mTask;
Dianne Hackborn247fe742011-01-08 17:25:57 -080083 volatile LoadTask mCancellingTask;
84
85 long mUpdateThrottle;
86 long mLastLoadCompleteTime = -10000;
87 Handler mHandler;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050088
89 public AsyncTaskLoader(Context context) {
90 super(context);
91 }
92
Dianne Hackborn247fe742011-01-08 17:25:57 -080093 /**
94 * Set amount to throttle updates by. This is the minimum time from
95 * when the last {@link #onLoadInBackground()} call has completed until
96 * a new load is scheduled.
97 *
98 * @param delayMS Amount of delay, in milliseconds.
99 */
100 public void setUpdateThrottle(long delayMS) {
101 mUpdateThrottle = delayMS;
102 if (delayMS != 0) {
103 mHandler = new Handler();
104 }
105 }
106
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500107 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800108 protected void onForceLoad() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800109 super.onForceLoad();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700110 cancelLoad();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500111 mTask = new LoadTask();
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800112 if (DEBUG) Slog.v(TAG, "Preparing load: mTask=" + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800113 executePendingTask();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500114 }
115
116 /**
117 * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
Dianne Hackborn247fe742011-01-08 17:25:57 -0800118 * for more info. Must be called on the main thread of the process.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500119 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800120 * <p>Cancelling is not an immediate operation, since the load is performed
121 * in a background thread. If there is currently a load in progress, this
122 * method requests that the load be cancelled, and notes this is the case;
123 * once the background thread has completed its work its remaining state
124 * will be cleared. If another load request comes in during this time,
125 * it will be held until the cancelled load is complete.
126 *
127 * @return Returns <tt>false</tt> if the task could not be cancelled,
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500128 * typically because it has already completed normally, or
Dianne Hackborn247fe742011-01-08 17:25:57 -0800129 * because {@link #startLoading()} hasn't been called; returns
130 * <tt>true</tt> otherwise.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500131 */
132 public boolean cancelLoad() {
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800133 if (DEBUG) Slog.v(TAG, "cancelLoad: mTask=" + mTask);
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500134 if (mTask != null) {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800135 if (mCancellingTask != null) {
136 // There was a pending task already waiting for a previous
137 // one being canceled; just drop it.
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800138 if (DEBUG) Slog.v(TAG,
139 "cancelLoad: still waiting for cancelled task; dropping next");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800140 if (mTask.waiting) {
141 mTask.waiting = false;
142 mHandler.removeCallbacks(mTask);
143 }
144 mTask = null;
145 return false;
146 } else if (mTask.waiting) {
147 // There is a task, but it is waiting for the time it should
148 // execute. We can just toss it.
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800149 if (DEBUG) Slog.v(TAG, "cancelLoad: task is waiting, dropping it");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800150 mTask.waiting = false;
151 mHandler.removeCallbacks(mTask);
152 mTask = null;
153 return false;
154 } else {
155 boolean cancelled = mTask.cancel(false);
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800156 if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800157 if (cancelled) {
158 mCancellingTask = mTask;
159 }
160 mTask = null;
161 return cancelled;
162 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500163 }
164 return false;
165 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700166
167 /**
168 * Called if the task was canceled before it was completed. Gives the class a chance
169 * to properly dispose of the result.
170 */
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800171 public void onCanceled(D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800172 }
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800173
Dianne Hackborn247fe742011-01-08 17:25:57 -0800174 void executePendingTask() {
175 if (mCancellingTask == null && mTask != null) {
176 if (mTask.waiting) {
177 mTask.waiting = false;
178 mHandler.removeCallbacks(mTask);
179 }
180 if (mUpdateThrottle > 0) {
181 long now = SystemClock.uptimeMillis();
182 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
183 // Not yet time to do another load.
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800184 if (DEBUG) Slog.v(TAG, "Waiting until "
185 + (mLastLoadCompleteTime+mUpdateThrottle)
186 + " to execute: " + mTask);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800187 mTask.waiting = true;
188 mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
189 return;
190 }
191 }
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800192 if (DEBUG) Slog.v(TAG, "Executing: " + mTask);
Dianne Hackborn5d9d03a2011-01-24 13:15:09 -0800193 mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800194 }
195 }
196
197 void dispatchOnCancelled(LoadTask task, D data) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800198 onCanceled(data);
Dianne Hackborn247fe742011-01-08 17:25:57 -0800199 if (mCancellingTask == task) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800200 if (DEBUG) Slog.v(TAG, "Cancelled task is now canceled!");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800201 mLastLoadCompleteTime = SystemClock.uptimeMillis();
202 mCancellingTask = null;
203 executePendingTask();
204 }
205 }
206
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800207 void dispatchOnLoadComplete(LoadTask task, D data) {
208 if (mTask != task) {
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800209 if (DEBUG) Slog.v(TAG, "Load complete of old task, trying to cancel");
Dianne Hackborn247fe742011-01-08 17:25:57 -0800210 dispatchOnCancelled(task, data);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800211 } else {
Dianne Hackborn260c3c72011-01-30 16:55:55 -0800212 if (isAbandoned()) {
213 // This cursor has been abandoned; just cancel the new data.
214 onCanceled(data);
215 } else {
216 mLastLoadCompleteTime = SystemClock.uptimeMillis();
217 mTask = null;
218 if (DEBUG) Slog.v(TAG, "Delivering result");
219 deliverResult(data);
220 }
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800221 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500222 }
223
224 /**
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500225 */
226 public abstract D loadInBackground();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800227
228 /**
Dianne Hackborn247fe742011-01-08 17:25:57 -0800229 * Called on a worker thread to perform the actual load. Implementations should not deliver the
230 * result directly, but should return them from this method, which will eventually end up
231 * calling {@link #deliverResult} on the UI thread. If implementations need to process
232 * the results on the UI thread they may override {@link #deliverResult} and do so
233 * there.
234 *
235 * @return Implementations must return the result of their load operation.
236 */
237 protected D onLoadInBackground() {
238 return loadInBackground();
239 }
240
241 /**
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800242 * Locks the current thread until the loader completes the current load
243 * operation. Returns immediately if there is no load operation running.
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800244 * Should not be called from the UI thread: calling it from the UI
245 * thread would cause a deadlock.
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800246 * <p>
Dianne Hackborn247fe742011-01-08 17:25:57 -0800247 * Use for testing only. <b>Never</b> call this from a UI thread.
Dmitri Plotnikov4afde4f2011-01-18 09:41:29 -0800248 *
249 * @hide
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800250 */
251 public void waitForLoader() {
252 LoadTask task = mTask;
253 if (task != null) {
254 try {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800255 task.done.await();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800256 } catch (InterruptedException e) {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800257 // Ignore
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800258 }
259 }
260 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800261
262 @Override
263 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
264 super.dump(prefix, fd, writer, args);
265 if (mTask != null) {
266 writer.print(prefix); writer.print("mTask="); writer.print(mTask);
267 writer.print(" waiting="); writer.println(mTask.waiting);
268 }
269 if (mCancellingTask != null) {
270 writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
271 writer.print(" waiting="); writer.println(mCancellingTask.waiting);
272 }
273 if (mUpdateThrottle != 0) {
274 writer.print(prefix); writer.print("mUpdateThrottle=");
275 TimeUtils.formatDuration(mUpdateThrottle, writer);
276 writer.print(" mLastLoadCompleteTime=");
277 TimeUtils.formatDuration(mLastLoadCompleteTime,
278 SystemClock.uptimeMillis(), writer);
279 writer.println();
280 }
281 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500282}