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