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