ModernAsyncTask.java revision fbabff99ccbcb576c713991c5db5dec21a0d0ce0
1/* 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.support.v4.content; 18 19import java.util.concurrent.BlockingQueue; 20import java.util.concurrent.Callable; 21import java.util.concurrent.CancellationException; 22import java.util.concurrent.ExecutionException; 23import java.util.concurrent.Executor; 24import java.util.concurrent.FutureTask; 25import java.util.concurrent.LinkedBlockingQueue; 26import java.util.concurrent.ThreadFactory; 27import java.util.concurrent.ThreadPoolExecutor; 28import java.util.concurrent.TimeUnit; 29import java.util.concurrent.TimeoutException; 30import java.util.concurrent.atomic.AtomicBoolean; 31import java.util.concurrent.atomic.AtomicInteger; 32 33import android.os.Handler; 34import android.os.Message; 35import android.os.Process; 36 37/** 38 * Copy of the required parts of AsyncTask from Android 3.0 that is needed 39 * to support AsyncTaskLoader. We use this rather than the one from the platform 40 * because we rely on some subtle behavior of AsyncTask that is not reliable on 41 * older platforms. 42 */ 43public abstract class ModernAsyncTask<Params, Progress, Result> { 44 private static final String LOG_TAG = "AsyncTask"; 45 46 private static final int CORE_POOL_SIZE = 5; 47 private static final int MAXIMUM_POOL_SIZE = 128; 48 private static final int KEEP_ALIVE = 1; 49 50 private static final ThreadFactory sThreadFactory = new ThreadFactory() { 51 private final AtomicInteger mCount = new AtomicInteger(1); 52 53 public Thread newThread(Runnable r) { 54 return new Thread(r, "ModernAsyncTask #" + mCount.getAndIncrement()); 55 } 56 }; 57 58 private static final BlockingQueue<Runnable> sPoolWorkQueue = 59 new LinkedBlockingQueue<Runnable>(10); 60 61 /** 62 * An {@link Executor} that can be used to execute tasks in parallel. 63 */ 64 public static final Executor THREAD_POOL_EXECUTOR 65 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 66 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 67 68 private static final int MESSAGE_POST_RESULT = 0x1; 69 private static final int MESSAGE_POST_PROGRESS = 0x2; 70 71 private static final InternalHandler sHandler = new InternalHandler(); 72 73 private static volatile Executor sDefaultExecutor = THREAD_POOL_EXECUTOR; 74 private final WorkerRunnable<Params, Result> mWorker; 75 private final FutureTask<Result> mFuture; 76 77 private volatile Status mStatus = Status.PENDING; 78 79 private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); 80 81 /** 82 * Indicates the current status of the task. Each status will be set only once 83 * during the lifetime of a task. 84 */ 85 public enum Status { 86 /** 87 * Indicates that the task has not been executed yet. 88 */ 89 PENDING, 90 /** 91 * Indicates that the task is running. 92 */ 93 RUNNING, 94 /** 95 * Indicates that {@link AsyncTask#onPostExecute} has finished. 96 */ 97 FINISHED, 98 } 99 100 /** @hide Used to force static handler to be created. */ 101 public static void init() { 102 sHandler.getLooper(); 103 } 104 105 /** @hide */ 106 public static void setDefaultExecutor(Executor exec) { 107 sDefaultExecutor = exec; 108 } 109 110 /** 111 * Creates a new asynchronous task. This constructor must be invoked on the UI thread. 112 */ 113 public ModernAsyncTask() { 114 mWorker = new WorkerRunnable<Params, Result>() { 115 public Result call() throws Exception { 116 mTaskInvoked.set(true); 117 118 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 119 return postResult(doInBackground(mParams)); 120 } 121 }; 122 123 mFuture = new FutureTask<Result>(mWorker) { 124 @Override 125 protected void done() { 126 try { 127 final Result result = get(); 128 129 postResultIfNotInvoked(result); 130 } catch (InterruptedException e) { 131 android.util.Log.w(LOG_TAG, e); 132 } catch (ExecutionException e) { 133 throw new RuntimeException("An error occured while executing doInBackground()", 134 e.getCause()); 135 } catch (CancellationException e) { 136 postResultIfNotInvoked(null); 137 } catch (Throwable t) { 138 throw new RuntimeException("An error occured while executing " 139 + "doInBackground()", t); 140 } 141 } 142 }; 143 } 144 145 private void postResultIfNotInvoked(Result result) { 146 final boolean wasTaskInvoked = mTaskInvoked.get(); 147 if (!wasTaskInvoked) { 148 postResult(result); 149 } 150 } 151 152 private Result postResult(Result result) { 153 Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, 154 new AsyncTaskResult<Result>(this, result)); 155 message.sendToTarget(); 156 return result; 157 } 158 159 /** 160 * Returns the current status of this task. 161 * 162 * @return The current status. 163 */ 164 public final Status getStatus() { 165 return mStatus; 166 } 167 168 /** 169 * Override this method to perform a computation on a background thread. The 170 * specified parameters are the parameters passed to {@link #execute} 171 * by the caller of this task. 172 * 173 * This method can call {@link #publishProgress} to publish updates 174 * on the UI thread. 175 * 176 * @param params The parameters of the task. 177 * 178 * @return A result, defined by the subclass of this task. 179 * 180 * @see #onPreExecute() 181 * @see #onPostExecute 182 * @see #publishProgress 183 */ 184 protected abstract Result doInBackground(Params... params); 185 186 /** 187 * Runs on the UI thread before {@link #doInBackground}. 188 * 189 * @see #onPostExecute 190 * @see #doInBackground 191 */ 192 protected void onPreExecute() { 193 } 194 195 /** 196 * <p>Runs on the UI thread after {@link #doInBackground}. The 197 * specified result is the value returned by {@link #doInBackground}.</p> 198 * 199 * <p>This method won't be invoked if the task was cancelled.</p> 200 * 201 * @param result The result of the operation computed by {@link #doInBackground}. 202 * 203 * @see #onPreExecute 204 * @see #doInBackground 205 * @see #onCancelled(Object) 206 */ 207 @SuppressWarnings({"UnusedDeclaration"}) 208 protected void onPostExecute(Result result) { 209 } 210 211 /** 212 * Runs on the UI thread after {@link #publishProgress} is invoked. 213 * The specified values are the values passed to {@link #publishProgress}. 214 * 215 * @param values The values indicating progress. 216 * 217 * @see #publishProgress 218 * @see #doInBackground 219 */ 220 @SuppressWarnings({"UnusedDeclaration"}) 221 protected void onProgressUpdate(Progress... values) { 222 } 223 224 /** 225 * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and 226 * {@link #doInBackground(Object[])} has finished.</p> 227 * 228 * <p>The default implementation simply invokes {@link #onCancelled()} and 229 * ignores the result. If you write your own implementation, do not call 230 * <code>super.onCancelled(result)</code>.</p> 231 * 232 * @param result The result, if any, computed in 233 * {@link #doInBackground(Object[])}, can be null 234 * 235 * @see #cancel(boolean) 236 * @see #isCancelled() 237 */ 238 @SuppressWarnings({"UnusedParameters"}) 239 protected void onCancelled(Result result) { 240 onCancelled(); 241 } 242 243 /** 244 * <p>Applications should preferably override {@link #onCancelled(Object)}. 245 * This method is invoked by the default implementation of 246 * {@link #onCancelled(Object)}.</p> 247 * 248 * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and 249 * {@link #doInBackground(Object[])} has finished.</p> 250 * 251 * @see #onCancelled(Object) 252 * @see #cancel(boolean) 253 * @see #isCancelled() 254 */ 255 protected void onCancelled() { 256 } 257 258 /** 259 * Returns <tt>true</tt> if this task was cancelled before it completed 260 * normally. If you are calling {@link #cancel(boolean)} on the task, 261 * the value returned by this method should be checked periodically from 262 * {@link #doInBackground(Object[])} to end the task as soon as possible. 263 * 264 * @return <tt>true</tt> if task was cancelled before it completed 265 * 266 * @see #cancel(boolean) 267 */ 268 public final boolean isCancelled() { 269 return mFuture.isCancelled(); 270 } 271 272 /** 273 * <p>Attempts to cancel execution of this task. This attempt will 274 * fail if the task has already completed, already been cancelled, 275 * or could not be cancelled for some other reason. If successful, 276 * and this task has not started when <tt>cancel</tt> is called, 277 * this task should never run. If the task has already started, 278 * then the <tt>mayInterruptIfRunning</tt> parameter determines 279 * whether the thread executing this task should be interrupted in 280 * an attempt to stop the task.</p> 281 * 282 * <p>Calling this method will result in {@link #onCancelled(Object)} being 283 * invoked on the UI thread after {@link #doInBackground(Object[])} 284 * returns. Calling this method guarantees that {@link #onPostExecute(Object)} 285 * is never invoked. After invoking this method, you should check the 286 * value returned by {@link #isCancelled()} periodically from 287 * {@link #doInBackground(Object[])} to finish the task as early as 288 * possible.</p> 289 * 290 * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this 291 * task should be interrupted; otherwise, in-progress tasks are allowed 292 * to complete. 293 * 294 * @return <tt>false</tt> if the task could not be cancelled, 295 * typically because it has already completed normally; 296 * <tt>true</tt> otherwise 297 * 298 * @see #isCancelled() 299 * @see #onCancelled(Object) 300 */ 301 public final boolean cancel(boolean mayInterruptIfRunning) { 302 return mFuture.cancel(mayInterruptIfRunning); 303 } 304 305 /** 306 * Waits if necessary for the computation to complete, and then 307 * retrieves its result. 308 * 309 * @return The computed result. 310 * 311 * @throws CancellationException If the computation was cancelled. 312 * @throws ExecutionException If the computation threw an exception. 313 * @throws InterruptedException If the current thread was interrupted 314 * while waiting. 315 */ 316 public final Result get() throws InterruptedException, ExecutionException { 317 return mFuture.get(); 318 } 319 320 /** 321 * Waits if necessary for at most the given time for the computation 322 * to complete, and then retrieves its result. 323 * 324 * @param timeout Time to wait before cancelling the operation. 325 * @param unit The time unit for the timeout. 326 * 327 * @return The computed result. 328 * 329 * @throws CancellationException If the computation was cancelled. 330 * @throws ExecutionException If the computation threw an exception. 331 * @throws InterruptedException If the current thread was interrupted 332 * while waiting. 333 * @throws TimeoutException If the wait timed out. 334 */ 335 public final Result get(long timeout, TimeUnit unit) throws InterruptedException, 336 ExecutionException, TimeoutException { 337 return mFuture.get(timeout, unit); 338 } 339 340 /** 341 * Executes the task with the specified parameters. The task returns 342 * itself (this) so that the caller can keep a reference to it. 343 * 344 * <p>Note: this function schedules the task on a queue for a single background 345 * thread or pool of threads depending on the platform version. When first 346 * introduced, AsyncTasks were executed serially on a single background thread. 347 * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed 348 * to a pool of threads allowing multiple tasks to operate in parallel. After 349 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, it is planned to change this 350 * back to a single thread to avoid common application errors caused 351 * by parallel execution. If you truly want parallel execution, you can use 352 * the {@link #executeOnExecutor} version of this method 353 * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on 354 * its use. 355 * 356 * <p>This method must be invoked on the UI thread. 357 * 358 * @param params The parameters of the task. 359 * 360 * @return This instance of AsyncTask. 361 * 362 * @throws IllegalStateException If {@link #getStatus()} returns either 363 * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. 364 */ 365 public final ModernAsyncTask<Params, Progress, Result> execute(Params... params) { 366 return executeOnExecutor(sDefaultExecutor, params); 367 } 368 369 /** 370 * Executes the task with the specified parameters. The task returns 371 * itself (this) so that the caller can keep a reference to it. 372 * 373 * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to 374 * allow multiple tasks to run in parallel on a pool of threads managed by 375 * AsyncTask, however you can also use your own {@link Executor} for custom 376 * behavior. 377 * 378 * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from 379 * a thread pool is generally <em>not</em> what one wants, because the order 380 * of their operation is not defined. For example, if these tasks are used 381 * to modify any state in common (such as writing a file due to a button click), 382 * there are no guarantees on the order of the modifications. 383 * Without careful work it is possible in rare cases for the newer version 384 * of the data to be over-written by an older one, leading to obscure data 385 * loss and stability issues. Such changes are best 386 * executed in serial; to guarantee such work is serialized regardless of 387 * platform version you can use this function with {@link #SERIAL_EXECUTOR}. 388 * 389 * <p>This method must be invoked on the UI thread. 390 * 391 * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a 392 * convenient process-wide thread pool for tasks that are loosely coupled. 393 * @param params The parameters of the task. 394 * 395 * @return This instance of AsyncTask. 396 * 397 * @throws IllegalStateException If {@link #getStatus()} returns either 398 * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. 399 */ 400 public final ModernAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, 401 Params... params) { 402 if (mStatus != Status.PENDING) { 403 switch (mStatus) { 404 case RUNNING: 405 throw new IllegalStateException("Cannot execute task:" 406 + " the task is already running."); 407 case FINISHED: 408 throw new IllegalStateException("Cannot execute task:" 409 + " the task has already been executed " 410 + "(a task can be executed only once)"); 411 } 412 } 413 414 mStatus = Status.RUNNING; 415 416 onPreExecute(); 417 418 mWorker.mParams = params; 419 exec.execute(mFuture); 420 421 return this; 422 } 423 424 /** 425 * Convenience version of {@link #execute(Object...)} for use with 426 * a simple Runnable object. 427 */ 428 public static void execute(Runnable runnable) { 429 sDefaultExecutor.execute(runnable); 430 } 431 432 /** 433 * This method can be invoked from {@link #doInBackground} to 434 * publish updates on the UI thread while the background computation is 435 * still running. Each call to this method will trigger the execution of 436 * {@link #onProgressUpdate} on the UI thread. 437 * 438 * {@link #onProgressUpdate} will note be called if the task has been 439 * canceled. 440 * 441 * @param values The progress values to update the UI with. 442 * 443 * @see #onProgressUpdate 444 * @see #doInBackground 445 */ 446 protected final void publishProgress(Progress... values) { 447 if (!isCancelled()) { 448 sHandler.obtainMessage(MESSAGE_POST_PROGRESS, 449 new AsyncTaskResult<Progress>(this, values)).sendToTarget(); 450 } 451 } 452 453 private void finish(Result result) { 454 if (isCancelled()) { 455 onCancelled(result); 456 } else { 457 onPostExecute(result); 458 } 459 mStatus = Status.FINISHED; 460 } 461 462 private static class InternalHandler extends Handler { 463 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 464 @Override 465 public void handleMessage(Message msg) { 466 AsyncTaskResult result = (AsyncTaskResult) msg.obj; 467 switch (msg.what) { 468 case MESSAGE_POST_RESULT: 469 // There is only one result 470 result.mTask.finish(result.mData[0]); 471 break; 472 case MESSAGE_POST_PROGRESS: 473 result.mTask.onProgressUpdate(result.mData); 474 break; 475 } 476 } 477 } 478 479 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { 480 Params[] mParams; 481 } 482 483 @SuppressWarnings({"RawUseOfParameterizedType"}) 484 private static class AsyncTaskResult<Data> { 485 final ModernAsyncTask mTask; 486 final Data[] mData; 487 488 AsyncTaskResult(ModernAsyncTask task, Data... data) { 489 mTask = task; 490 mData = data; 491 } 492 } 493} 494