blob: 98710558ce18c2b7ac68557b9e4d13c771340b7b [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 Hackborn247fe742011-01-08 17:25:57 -080022import android.util.TimeUtils;
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080023
Dianne Hackborn247fe742011-01-08 17:25:57 -080024import java.io.FileDescriptor;
25import java.io.PrintWriter;
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080026import java.util.concurrent.CountDownLatch;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050027
28/**
29 * Abstract Loader that provides an {@link AsyncTask} to do the work.
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070030 *
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050031 * @param <D> the data type to be loaded.
32 */
33public abstract class AsyncTaskLoader<D> extends Loader<D> {
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080034
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080035
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080036 private static final String TAG = "AsyncTaskLoader";
37
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 Hackborn247fe742011-01-08 17:25:57 -080048 result = AsyncTaskLoader.this.onLoadInBackground();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070049 return result;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050050 }
51
52 /* Runs on the UI thread */
53 @Override
54 protected void onPostExecute(D data) {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080055 try {
56 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
57 } finally {
58 done.countDown();
59 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050060 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070061
62 @Override
63 protected void onCancelled() {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -080064 try {
65 AsyncTaskLoader.this.dispatchOnCancelled(this, result);
66 } finally {
67 done.countDown();
68 }
Dianne Hackborn247fe742011-01-08 17:25:57 -080069 }
70
71 @Override
72 public void run() {
73 waiting = false;
74 AsyncTaskLoader.this.executePendingTask();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -070075 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050076 }
77
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -080078 volatile LoadTask mTask;
Dianne Hackborn247fe742011-01-08 17:25:57 -080079 volatile LoadTask mCancellingTask;
80
81 long mUpdateThrottle;
82 long mLastLoadCompleteTime = -10000;
83 Handler mHandler;
Jeff Hamilton9911b7f2010-05-15 02:20:31 -050084
85 public AsyncTaskLoader(Context context) {
86 super(context);
87 }
88
Dianne Hackborn247fe742011-01-08 17:25:57 -080089 /**
90 * Set amount to throttle updates by. This is the minimum time from
91 * when the last {@link #onLoadInBackground()} call has completed until
92 * a new load is scheduled.
93 *
94 * @param delayMS Amount of delay, in milliseconds.
95 */
96 public void setUpdateThrottle(long delayMS) {
97 mUpdateThrottle = delayMS;
98 if (delayMS != 0) {
99 mHandler = new Handler();
100 }
101 }
102
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500103 @Override
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800104 protected void onForceLoad() {
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800105 super.onForceLoad();
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700106 cancelLoad();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500107 mTask = new LoadTask();
Dianne Hackborn247fe742011-01-08 17:25:57 -0800108 executePendingTask();
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500109 }
110
111 /**
112 * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
Dianne Hackborn247fe742011-01-08 17:25:57 -0800113 * for more info. Must be called on the main thread of the process.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500114 *
Dianne Hackborn247fe742011-01-08 17:25:57 -0800115 * <p>Cancelling is not an immediate operation, since the load is performed
116 * in a background thread. If there is currently a load in progress, this
117 * method requests that the load be cancelled, and notes this is the case;
118 * once the background thread has completed its work its remaining state
119 * will be cleared. If another load request comes in during this time,
120 * it will be held until the cancelled load is complete.
121 *
122 * @return Returns <tt>false</tt> if the task could not be cancelled,
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500123 * typically because it has already completed normally, or
Dianne Hackborn247fe742011-01-08 17:25:57 -0800124 * because {@link #startLoading()} hasn't been called; returns
125 * <tt>true</tt> otherwise.
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500126 */
127 public boolean cancelLoad() {
128 if (mTask != null) {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800129 if (mCancellingTask != null) {
130 // There was a pending task already waiting for a previous
131 // one being canceled; just drop it.
132 if (mTask.waiting) {
133 mTask.waiting = false;
134 mHandler.removeCallbacks(mTask);
135 }
136 mTask = null;
137 return false;
138 } else if (mTask.waiting) {
139 // There is a task, but it is waiting for the time it should
140 // execute. We can just toss it.
141 mTask.waiting = false;
142 mHandler.removeCallbacks(mTask);
143 mTask = null;
144 return false;
145 } else {
146 boolean cancelled = mTask.cancel(false);
147 if (cancelled) {
148 mCancellingTask = mTask;
149 }
150 mTask = null;
151 return cancelled;
152 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500153 }
154 return false;
155 }
Dmitri Plotnikovbef9c7a2010-06-16 15:38:07 -0700156
157 /**
158 * Called if the task was canceled before it was completed. Gives the class a chance
159 * to properly dispose of the result.
160 */
161 public void onCancelled(D data) {
162 }
163
Dianne Hackborn247fe742011-01-08 17:25:57 -0800164 void executePendingTask() {
165 if (mCancellingTask == null && mTask != null) {
166 if (mTask.waiting) {
167 mTask.waiting = false;
168 mHandler.removeCallbacks(mTask);
169 }
170 if (mUpdateThrottle > 0) {
171 long now = SystemClock.uptimeMillis();
172 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
173 // Not yet time to do another load.
174 mTask.waiting = true;
175 mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
176 return;
177 }
178 }
179 mTask.execute((Void[]) null);
180 }
181 }
182
183 void dispatchOnCancelled(LoadTask task, D data) {
184 onCancelled(data);
185 if (mCancellingTask == task) {
186 mLastLoadCompleteTime = SystemClock.uptimeMillis();
187 mCancellingTask = null;
188 executePendingTask();
189 }
190 }
191
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800192 void dispatchOnLoadComplete(LoadTask task, D data) {
193 if (mTask != task) {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800194 dispatchOnCancelled(task, data);
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800195 } else {
Dianne Hackborn247fe742011-01-08 17:25:57 -0800196 mLastLoadCompleteTime = SystemClock.uptimeMillis();
Dianne Hackborn0e3b8f422010-12-20 23:22:11 -0800197 mTask = null;
198 deliverResult(data);
199 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500200 }
201
202 /**
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500203 */
204 public abstract D loadInBackground();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800205
206 /**
Dianne Hackborn247fe742011-01-08 17:25:57 -0800207 * Called on a worker thread to perform the actual load. Implementations should not deliver the
208 * result directly, but should return them from this method, which will eventually end up
209 * calling {@link #deliverResult} on the UI thread. If implementations need to process
210 * the results on the UI thread they may override {@link #deliverResult} and do so
211 * there.
212 *
213 * @return Implementations must return the result of their load operation.
214 */
215 protected D onLoadInBackground() {
216 return loadInBackground();
217 }
218
219 /**
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800220 * Locks the current thread until the loader completes the current load
221 * operation. Returns immediately if there is no load operation running.
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800222 * Should not be called from the UI thread: calling it from the UI
223 * thread would cause a deadlock.
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800224 * <p>
Dianne Hackborn247fe742011-01-08 17:25:57 -0800225 * Use for testing only. <b>Never</b> call this from a UI thread.
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800226 */
227 public void waitForLoader() {
228 LoadTask task = mTask;
229 if (task != null) {
230 try {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800231 task.done.await();
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800232 } catch (InterruptedException e) {
Dmitri Plotnikov59d8edd2011-01-09 11:05:50 -0800233 // Ignore
Dmitri Plotnikovcd3676e2011-01-06 18:39:33 -0800234 }
235 }
236 }
Dianne Hackborn247fe742011-01-08 17:25:57 -0800237
238 @Override
239 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
240 super.dump(prefix, fd, writer, args);
241 if (mTask != null) {
242 writer.print(prefix); writer.print("mTask="); writer.print(mTask);
243 writer.print(" waiting="); writer.println(mTask.waiting);
244 }
245 if (mCancellingTask != null) {
246 writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
247 writer.print(" waiting="); writer.println(mCancellingTask.waiting);
248 }
249 if (mUpdateThrottle != 0) {
250 writer.print(prefix); writer.print("mUpdateThrottle=");
251 TimeUtils.formatDuration(mUpdateThrottle, writer);
252 writer.print(" mLastLoadCompleteTime=");
253 TimeUtils.formatDuration(mLastLoadCompleteTime,
254 SystemClock.uptimeMillis(), writer);
255 writer.println();
256 }
257 }
Jeff Hamilton9911b7f2010-05-15 02:20:31 -0500258}