switch to alsa.omap3 module
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / content / AsyncTaskLoader.java
CommitLineData
811a5a4a 1/*
2 * Copyright (C) 2011 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.support.v2.content;
18
19import android.content.Context;
20import android.os.AsyncTask;
21import android.os.Handler;
22import android.os.SystemClock;
23import android.support.v2.util.TimeUtils;
24import android.util.Log;
25
26import java.io.FileDescriptor;
27import java.io.PrintWriter;
28import java.util.concurrent.CountDownLatch;
29
30/**
31 * Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
32 * Used to write apps that run on platforms prior to Android 3.0. When running
33 * on Android 3.0 or above, this implementation is still used; it does not try
34 * to switch to the framework's implementation. See the framework SDK
35 * documentation for a class overview.
36 */
37public abstract class AsyncTaskLoader<D> extends Loader<D> {
38 static final String TAG = "AsyncTaskLoader";
39 static final boolean DEBUG = false;
40
41 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
42
43 D result;
44 boolean waiting;
45
46 private CountDownLatch done = new CountDownLatch(1);
47
48 /* Runs on a worker thread */
49 @Override
50 protected D doInBackground(Void... params) {
51 if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
52 result = AsyncTaskLoader.this.onLoadInBackground();
53 if (DEBUG) Log.v(TAG, this + " <<< doInBackground");
54 return result;
55 }
56
57 /* Runs on the UI thread */
58 @Override
59 protected void onPostExecute(D data) {
60 if (DEBUG) Log.v(TAG, this + " onPostExecute");
61 try {
62 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
63 } finally {
64 done.countDown();
65 }
66 }
67
68 @Override
69 protected void onCancelled() {
70 if (DEBUG) Log.v(TAG, this + " onCancelled");
71 try {
72 AsyncTaskLoader.this.dispatchOnCancelled(this, result);
73 } finally {
74 done.countDown();
75 }
76 }
77
78 @Override
79 public void run() {
80 waiting = false;
81 AsyncTaskLoader.this.executePendingTask();
82 }
83 }
84
85 volatile LoadTask mTask;
86 volatile LoadTask mCancellingTask;
87
88 long mUpdateThrottle;
89 long mLastLoadCompleteTime = -10000;
90 Handler mHandler;
91
92 public AsyncTaskLoader(Context context) {
93 super(context);
94 }
95
96 /**
97 * Set amount to throttle updates by. This is the minimum time from
98 * when the last {@link #onLoadInBackground()} call has completed until
99 * a new load is scheduled.
100 *
101 * @param delayMS Amount of delay, in milliseconds.
102 */
103 public void setUpdateThrottle(long delayMS) {
104 mUpdateThrottle = delayMS;
105 if (delayMS != 0) {
106 mHandler = new Handler();
107 }
108 }
109
110 @Override
111 protected void onForceLoad() {
112 super.onForceLoad();
113 cancelLoad();
114 mTask = new LoadTask();
115 if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
116 executePendingTask();
117 }
118
119 /**
120 * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
121 * for more info. Must be called on the main thread of the process.
122 *
123 * <p>Cancelling is not an immediate operation, since the load is performed
124 * in a background thread. If there is currently a load in progress, this
125 * method requests that the load be cancelled, and notes this is the case;
126 * once the background thread has completed its work its remaining state
127 * will be cleared. If another load request comes in during this time,
128 * it will be held until the cancelled load is complete.
129 *
130 * @return Returns <tt>false</tt> if the task could not be cancelled,
131 * typically because it has already completed normally, or
132 * because {@link #startLoading()} hasn't been called; returns
133 * <tt>true</tt> otherwise.
134 */
135 public boolean cancelLoad() {
136 if (DEBUG) Log.v(TAG, "cancelLoad: mTask=" + mTask);
137 if (mTask != null) {
138 if (mCancellingTask != null) {
139 // There was a pending task already waiting for a previous
140 // one being canceled; just drop it.
141 if (DEBUG) Log.v(TAG,
142 "cancelLoad: still waiting for cancelled task; dropping next");
143 if (mTask.waiting) {
144 mTask.waiting = false;
145 mHandler.removeCallbacks(mTask);
146 }
147 mTask = null;
148 return false;
149 } else if (mTask.waiting) {
150 // There is a task, but it is waiting for the time it should
151 // execute. We can just toss it.
152 if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
153 mTask.waiting = false;
154 mHandler.removeCallbacks(mTask);
155 mTask = null;
156 return false;
157 } else {
158 boolean cancelled = mTask.cancel(false);
159 if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
160 if (cancelled) {
161 mCancellingTask = mTask;
162 }
163 mTask = null;
164 return cancelled;
165 }
166 }
167 return false;
168 }
169
170 /**
171 * Called if the task was canceled before it was completed. Gives the class a chance
172 * to properly dispose of the result.
173 */
174 public void onCanceled(D data) {
175 }
176
177 void executePendingTask() {
178 if (mCancellingTask == null && mTask != null) {
179 if (mTask.waiting) {
180 mTask.waiting = false;
181 mHandler.removeCallbacks(mTask);
182 }
183 if (mUpdateThrottle > 0) {
184 long now = SystemClock.uptimeMillis();
185 if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
186 // Not yet time to do another load.
187 if (DEBUG) Log.v(TAG, "Waiting until "
188 + (mLastLoadCompleteTime+mUpdateThrottle)
189 + " to execute: " + mTask);
190 mTask.waiting = true;
191 mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
192 return;
193 }
194 }
195 if (DEBUG) Log.v(TAG, "Executing: " + mTask);
196 mTask.execute((Void[]) null);
197 // XXX TO DO: use reflection to call this version.
198 //mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
199 }
200 }
201
202 void dispatchOnCancelled(LoadTask task, D data) {
203 onCanceled(data);
204 if (mCancellingTask == task) {
205 if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
206 mLastLoadCompleteTime = SystemClock.uptimeMillis();
207 mCancellingTask = null;
208 executePendingTask();
209 }
210 }
211
212 void dispatchOnLoadComplete(LoadTask task, D data) {
213 if (mTask != task) {
214 if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
215 dispatchOnCancelled(task, data);
216 } else {
217 if (isAbandoned()) {
218 // This cursor has been abandoned; just cancel the new data.
219 onCanceled(data);
220 } else {
221 mLastLoadCompleteTime = SystemClock.uptimeMillis();
222 mTask = null;
223 if (DEBUG) Log.v(TAG, "Delivering result");
224 deliverResult(data);
225 }
226 }
227 }
228
229 /**
230 */
231 public abstract D loadInBackground();
232
233 /**
234 * Called on a worker thread to perform the actual load. Implementations should not deliver the
235 * result directly, but should return them from this method, which will eventually end up
236 * calling {@link #deliverResult} on the UI thread. If implementations need to process
237 * the results on the UI thread they may override {@link #deliverResult} and do so
238 * there.
239 *
240 * @return Implementations must return the result of their load operation.
241 */
242 protected D onLoadInBackground() {
243 return loadInBackground();
244 }
245
246 /**
247 * Locks the current thread until the loader completes the current load
248 * operation. Returns immediately if there is no load operation running.
249 * Should not be called from the UI thread: calling it from the UI
250 * thread would cause a deadlock.
251 * <p>
252 * Use for testing only. <b>Never</b> call this from a UI thread.
253 *
254 * @hide
255 */
256 public void waitForLoader() {
257 LoadTask task = mTask;
258 if (task != null) {
259 try {
260 task.done.await();
261 } catch (InterruptedException e) {
262 // Ignore
263 }
264 }
265 }
266
267 @Override
268 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
269 super.dump(prefix, fd, writer, args);
270 if (mTask != null) {
271 writer.print(prefix); writer.print("mTask="); writer.print(mTask);
272 writer.print(" waiting="); writer.println(mTask.waiting);
273 }
274 if (mCancellingTask != null) {
275 writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
276 writer.print(" waiting="); writer.println(mCancellingTask.waiting);
277 }
278 if (mUpdateThrottle != 0) {
279 writer.print(prefix); writer.print("mUpdateThrottle=");
280 TimeUtils.formatDuration(mUpdateThrottle, writer);
281 writer.print(" mLastLoadCompleteTime=");
282 TimeUtils.formatDuration(mLastLoadCompleteTime,
283 SystemClock.uptimeMillis(), writer);
284 writer.println();
285 }
286 }
287}