AsyncTask.java revision 5e7f2d0b227df38f6dbc2313ac8b5c9f225c135e
1/* 2 * Copyright (C) 2008 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.os; 18 19import java.util.ArrayDeque; 20import java.util.concurrent.BlockingQueue; 21import java.util.concurrent.Callable; 22import java.util.concurrent.CancellationException; 23import java.util.concurrent.Executor; 24import java.util.concurrent.ExecutionException; 25import java.util.concurrent.FutureTask; 26import java.util.concurrent.LinkedBlockingQueue; 27import java.util.concurrent.ThreadFactory; 28import java.util.concurrent.ThreadPoolExecutor; 29import java.util.concurrent.TimeUnit; 30import java.util.concurrent.TimeoutException; 31import java.util.concurrent.atomic.AtomicBoolean; 32import java.util.concurrent.atomic.AtomicInteger; 33 34/** 35 * <p>AsyncTask enables proper and easy use of the UI thread (also called main thread) or 36 * any other looper thread. AsyncTask is most commonly used to interact with the UI thread. 37 * This class allows to perform background operations and publish results on a looper 38 * thread without having to manipulate threads and/or handlers.</p> 39 * 40 * <p>An asynchronous task is defined by a computation that runs on a background thread and 41 * whose result is published on a looper thread. An asynchronous task is defined by 3 generic 42 * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>, 43 * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>, 44 * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p> 45 * 46 * <div class="special reference"> 47 * <h3>Developer Guides</h3> 48 * <p>For more information about using tasks and threads, read the 49 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and 50 * Threads</a> developer guide.</p> 51 * </div> 52 * 53 * <h2>Usage</h2> 54 * <p>AsyncTask must be subclassed to be used. The subclass will override at least 55 * one method ({@link #doInBackground}), and most often will override a 56 * second one ({@link #onPostExecute}.)</p> 57 * 58 * <p>Here is an example of subclassing:</p> 59 * <pre class="prettyprint"> 60 * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { 61 * protected Long doInBackground(URL... urls) { 62 * int count = urls.length; 63 * long totalSize = 0; 64 * for (int i = 0; i < count; i++) { 65 * totalSize += Downloader.downloadFile(urls[i]); 66 * publishProgress((int) ((i / (float) count) * 100)); 67 * } 68 * return totalSize; 69 * } 70 * 71 * protected void onProgressUpdate(Integer... progress) { 72 * setProgressPercent(progress[0]); 73 * } 74 * 75 * protected void onPostExecute(Long result) { 76 * showDialog("Downloaded " + result + " bytes"); 77 * } 78 * } 79 * </pre> 80 * 81 * <p>Once created, a task is executed very simply:</p> 82 * <pre class="prettyprint"> 83 * new DownloadFilesTask().execute(url1, url2, url3); 84 * </pre> 85 * 86 * <h2>AsyncTask's generic types</h2> 87 * <p>The three types used by an asynchronous task are the following:</p> 88 * <ol> 89 * <li><code>Params</code>, the type of the parameters sent to the task upon 90 * execution.</li> 91 * <li><code>Progress</code>, the type of the progress units published during 92 * the background computation.</li> 93 * <li><code>Result</code>, the type of the result of the background 94 * computation.</li> 95 * </ol> 96 * <p>Not all types are always used by an asynchronous task. To mark a type as unused, 97 * simply use the type {@link Void}:</p> 98 * <pre> 99 * private class MyTask extends AsyncTask<Void, Void, Void> { ... } 100 * </pre> 101 * 102 * <h2>The 4 steps</h2> 103 * <p>When an asynchronous task is executed, the task goes through 4 steps:</p> 104 * <ol> 105 * <li>{@link #onPreExecute()}, invoked on the looper thread immediately after the task 106 * is executed. This step is normally used to setup the task, for instance by 107 * showing a progress bar in the user interface.</li> 108 * <li>{@link #doInBackground}, invoked on the background thread 109 * immediately after {@link #onPreExecute()} finishes executing. This step is used 110 * to perform background computation that can take a long time. The parameters 111 * of the asynchronous task are passed to this step. The result of the computation must 112 * be returned by this step and will be passed back to the last step. This step 113 * can also use {@link #publishProgress} to publish one or more units 114 * of progress. These values are published on the looper thread, in the 115 * {@link #onProgressUpdate} step.</li> 116 * <li>{@link #onProgressUpdate}, invoked on the looper thread after a 117 * call to {@link #publishProgress}. The timing of the execution is 118 * undefined. This method is used to display any form of progress in the user 119 * interface while the background computation is still executing. For instance, 120 * it can be used to animate a progress bar or show logs in a text field.</li> 121 * <li>{@link #onPostExecute}, invoked on the looper thread after the background 122 * computation finishes. The result of the background computation is passed to 123 * this step as a parameter.</li> 124 * </ol> 125 * 126 * <h2>Cancelling a task</h2> 127 * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking 128 * this method will cause subsequent calls to {@link #isCancelled()} to return true. 129 * After invoking this method, {@link #onCancelled(Object)}, instead of 130 * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])} 131 * returns. To ensure that a task is cancelled as quickly as possible, you should always 132 * check the return value of {@link #isCancelled()} periodically from 133 * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p> 134 * 135 * <h2>Threading rules</h2> 136 * <p>There are a few threading rules that must be followed for this class to 137 * work properly:</p> 138 * <ul> 139 * <li>The task instance must be created on the looper thread.</li> 140 * <li>{@link #execute} must be invoked on the looper thread.</li> 141 * <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute}, 142 * {@link #doInBackground}, {@link #onProgressUpdate} manually.</li> 143 * <li>The task can be executed only once (an exception will be thrown if 144 * a second execution is attempted.)</li> 145 * </ul> 146 * 147 * <h2>Memory observability</h2> 148 * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following 149 * operations are safe without explicit synchronizations.</p> 150 * <ul> 151 * <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them 152 * in {@link #doInBackground}. 153 * <li>Set member fields in {@link #doInBackground}, and refer to them in 154 * {@link #onProgressUpdate} and {@link #onPostExecute}. 155 * </ul> 156 * 157 * @see Looper 158 * @see Handler 159 */ 160public abstract class AsyncTask<Params, Progress, Result> { 161 private static final String LOG_TAG = "AsyncTask"; 162 163 private static final int CORE_POOL_SIZE = 5; 164 private static final int MAXIMUM_POOL_SIZE = 128; 165 private static final int KEEP_ALIVE = 1; 166 167 private static final ThreadFactory sThreadFactory = new ThreadFactory() { 168 private final AtomicInteger mCount = new AtomicInteger(1); 169 170 public Thread newThread(Runnable r) { 171 return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); 172 } 173 }; 174 175 private static final BlockingQueue<Runnable> sPoolWorkQueue = 176 new LinkedBlockingQueue<Runnable>(10); 177 178 /** 179 * An {@link Executor} that can be used to execute tasks in parallel. 180 */ 181 public static final Executor THREAD_POOL_EXECUTOR 182 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 183 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 184 185 /** 186 * An {@link Executor} that executes tasks one at a time in serial 187 * order. This serialization is global to a particular process. 188 */ 189 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 190 191 private static final int MESSAGE_POST_RESULT = 0x1; 192 private static final int MESSAGE_POST_PROGRESS = 0x2; 193 194 private static final ThreadLocal<InternalHandler> sHandler = 195 new ThreadLocal<InternalHandler>() { 196 @Override 197 protected InternalHandler initialValue() { 198 return new InternalHandler(); 199 } 200 }; 201 202 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 203 private final WorkerRunnable<Params, Result> mWorker; 204 private final FutureTask<Result> mFuture; 205 206 private volatile Status mStatus = Status.PENDING; 207 208 private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); 209 private final InternalHandler mHandler; 210 211 private static class SerialExecutor implements Executor { 212 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 213 Runnable mActive; 214 215 public synchronized void execute(final Runnable r) { 216 mTasks.offer(new Runnable() { 217 public void run() { 218 try { 219 r.run(); 220 } finally { 221 scheduleNext(); 222 } 223 } 224 }); 225 if (mActive == null) { 226 scheduleNext(); 227 } 228 } 229 230 protected synchronized void scheduleNext() { 231 if ((mActive = mTasks.poll()) != null) { 232 THREAD_POOL_EXECUTOR.execute(mActive); 233 } 234 } 235 } 236 237 /** 238 * Indicates the current status of the task. Each status will be set only once 239 * during the lifetime of a task. 240 */ 241 public enum Status { 242 /** 243 * Indicates that the task has not been executed yet. 244 */ 245 PENDING, 246 /** 247 * Indicates that the task is running. 248 */ 249 RUNNING, 250 /** 251 * Indicates that {@link AsyncTask#onPostExecute} has finished. 252 */ 253 FINISHED, 254 } 255 256 /** @hide */ 257 public static void init() { 258 } 259 260 /** @hide */ 261 public static void setDefaultExecutor(Executor exec) { 262 sDefaultExecutor = exec; 263 } 264 265 /** 266 * Creates a new asynchronous task. This constructor must be invoked on the looper thread. 267 * 268 * @throws IllegalStateException if this constructor is invoked on a non-looper thread 269 * 270 * @see Looper 271 */ 272 public AsyncTask() { 273 if (Looper.myLooper() == null) { 274 throw new IllegalStateException("AsyncTask can be only instanciated on a " 275 + "looper thread. The current thread is " + Thread.currentThread()); 276 } 277 278 mHandler = sHandler.get(); 279 280 mWorker = new WorkerRunnable<Params, Result>() { 281 public Result call() throws Exception { 282 mTaskInvoked.set(true); 283 284 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 285 return postResult(doInBackground(mParams)); 286 } 287 }; 288 289 mFuture = new FutureTask<Result>(mWorker) { 290 @Override 291 protected void done() { 292 try { 293 final Result result = get(); 294 295 postResultIfNotInvoked(result); 296 } catch (InterruptedException e) { 297 android.util.Log.w(LOG_TAG, e); 298 } catch (ExecutionException e) { 299 throw new RuntimeException("An error occured while executing doInBackground()", 300 e.getCause()); 301 } catch (CancellationException e) { 302 postResultIfNotInvoked(null); 303 } catch (Throwable t) { 304 throw new RuntimeException("An error occured while executing " 305 + "doInBackground()", t); 306 } 307 } 308 }; 309 } 310 311 private void postResultIfNotInvoked(Result result) { 312 final boolean wasTaskInvoked = mTaskInvoked.get(); 313 if (!wasTaskInvoked) { 314 postResult(result); 315 } 316 } 317 318 private Result postResult(Result result) { 319<<<<<<< HEAD 320 Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, 321======= 322 @SuppressWarnings({"unchecked"}) 323 Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT, 324>>>>>>> 6c0d0b8... Check whether an AsyncTask is created/executed on a looper thread. 325 new AsyncTaskResult<Result>(this, result)); 326 message.sendToTarget(); 327 return result; 328 } 329 330 /** 331 * Returns the current status of this task. 332 * 333 * @return The current status. 334 */ 335 public final Status getStatus() { 336 return mStatus; 337 } 338 339 /** 340 * Override this method to perform a computation on a background thread. The 341 * specified parameters are the parameters passed to {@link #execute} 342 * by the caller of this task. 343 * 344 * This method can call {@link #publishProgress} to publish updates 345 * on the looper thread. 346 * 347 * @param params The parameters of the task. 348 * 349 * @return A result, defined by the subclass of this task. 350 * 351 * @see #onPreExecute() 352 * @see #onPostExecute 353 * @see #publishProgress 354 */ 355 protected abstract Result doInBackground(Params... params); 356 357 /** 358 * Runs on the looper thread before {@link #doInBackground}. 359 * 360 * @see #onPostExecute 361 * @see #doInBackground 362 */ 363 protected void onPreExecute() { 364 } 365 366 /** 367 * <p>Runs on the looper thread after {@link #doInBackground}. The 368 * specified result is the value returned by {@link #doInBackground}.</p> 369 * 370 * <p>This method won't be invoked if the task was cancelled.</p> 371 * 372 * @param result The result of the operation computed by {@link #doInBackground}. 373 * 374 * @see #onPreExecute 375 * @see #doInBackground 376 * @see #onCancelled(Object) 377 */ 378 @SuppressWarnings({"UnusedDeclaration"}) 379 protected void onPostExecute(Result result) { 380 } 381 382 /** 383 * Runs on the looper thread after {@link #publishProgress} is invoked. 384 * The specified values are the values passed to {@link #publishProgress}. 385 * 386 * @param values The values indicating progress. 387 * 388 * @see #publishProgress 389 * @see #doInBackground 390 */ 391 @SuppressWarnings({"UnusedDeclaration"}) 392 protected void onProgressUpdate(Progress... values) { 393 } 394 395 /** 396 * <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and 397 * {@link #doInBackground(Object[])} has finished.</p> 398 * 399 * <p>The default implementation simply invokes {@link #onCancelled()} and 400 * ignores the result. If you write your own implementation, do not call 401 * <code>super.onCancelled(result)</code>.</p> 402 * 403 * @param result The result, if any, computed in 404 * {@link #doInBackground(Object[])}, can be null 405 * 406 * @see #cancel(boolean) 407 * @see #isCancelled() 408 */ 409 @SuppressWarnings({"UnusedParameters"}) 410 protected void onCancelled(Result result) { 411 onCancelled(); 412 } 413 414 /** 415 * <p>Applications should preferably override {@link #onCancelled(Object)}. 416 * This method is invoked by the default implementation of 417 * {@link #onCancelled(Object)}.</p> 418 * 419 * <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and 420 * {@link #doInBackground(Object[])} has finished.</p> 421 * 422 * @see #onCancelled(Object) 423 * @see #cancel(boolean) 424 * @see #isCancelled() 425 */ 426 protected void onCancelled() { 427 } 428 429 /** 430 * Returns <tt>true</tt> if this task was cancelled before it completed 431 * normally. If you are calling {@link #cancel(boolean)} on the task, 432 * the value returned by this method should be checked periodically from 433 * {@link #doInBackground(Object[])} to end the task as soon as possible. 434 * 435 * @return <tt>true</tt> if task was cancelled before it completed 436 * 437 * @see #cancel(boolean) 438 */ 439 public final boolean isCancelled() { 440 return mFuture.isCancelled(); 441 } 442 443 /** 444 * <p>Attempts to cancel execution of this task. This attempt will 445 * fail if the task has already completed, already been cancelled, 446 * or could not be cancelled for some other reason. If successful, 447 * and this task has not started when <tt>cancel</tt> is called, 448 * this task should never run. If the task has already started, 449 * then the <tt>mayInterruptIfRunning</tt> parameter determines 450 * whether the thread executing this task should be interrupted in 451 * an attempt to stop the task.</p> 452 * 453 * <p>Calling this method will result in {@link #onCancelled(Object)} being 454 * invoked on the looper thread after {@link #doInBackground(Object[])} 455 * returns. Calling this method guarantees that {@link #onPostExecute(Object)} 456 * is never invoked. After invoking this method, you should check the 457 * value returned by {@link #isCancelled()} periodically from 458 * {@link #doInBackground(Object[])} to finish the task as early as 459 * possible.</p> 460 * 461 * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this 462 * task should be interrupted; otherwise, in-progress tasks are allowed 463 * to complete. 464 * 465 * @return <tt>false</tt> if the task could not be cancelled, 466 * typically because it has already completed normally; 467 * <tt>true</tt> otherwise 468 * 469 * @see #isCancelled() 470 * @see #onCancelled(Object) 471 */ 472 public final boolean cancel(boolean mayInterruptIfRunning) { 473 return mFuture.cancel(mayInterruptIfRunning); 474 } 475 476 /** 477 * Waits if necessary for the computation to complete, and then 478 * retrieves its result. 479 * 480 * @return The computed result. 481 * 482 * @throws CancellationException If the computation was cancelled. 483 * @throws ExecutionException If the computation threw an exception. 484 * @throws InterruptedException If the current thread was interrupted 485 * while waiting. 486 */ 487 public final Result get() throws InterruptedException, ExecutionException { 488 return mFuture.get(); 489 } 490 491 /** 492 * Waits if necessary for at most the given time for the computation 493 * to complete, and then retrieves its result. 494 * 495 * @param timeout Time to wait before cancelling the operation. 496 * @param unit The time unit for the timeout. 497 * 498 * @return The computed result. 499 * 500 * @throws CancellationException If the computation was cancelled. 501 * @throws ExecutionException If the computation threw an exception. 502 * @throws InterruptedException If the current thread was interrupted 503 * while waiting. 504 * @throws TimeoutException If the wait timed out. 505 */ 506 public final Result get(long timeout, TimeUnit unit) throws InterruptedException, 507 ExecutionException, TimeoutException { 508 return mFuture.get(timeout, unit); 509 } 510 511 /** 512 * Executes the task with the specified parameters. The task returns 513 * itself (this) so that the caller can keep a reference to it. 514 * 515 * <p>Note: this function schedules the task on a queue for a single background 516 * thread or pool of threads depending on the platform version. When first 517 * introduced, AsyncTasks were executed serially on a single background thread. 518 * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed 519 * to a pool of threads allowing multiple tasks to operate in parallel. After 520 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, it is planned to change this 521 * back to a single thread to avoid common application errors caused 522 * by parallel execution. If you truly want parallel execution, you can use 523 * the {@link #executeOnExecutor} version of this method 524 * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on 525 * its use. 526 * 527 * <p>This method must be invoked on the looper thread. 528 * 529 * @param params The parameters of the task. 530 * 531 * @return This instance of AsyncTask. 532 * 533 * @throws IllegalStateException If {@link #getStatus()} returns either 534 * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or 535 * the current thread is not a looper thread. 536 */ 537 public final AsyncTask<Params, Progress, Result> execute(Params... params) { 538 return executeOnExecutor(sDefaultExecutor, params); 539 } 540 541 /** 542 * Executes the task with the specified parameters. The task returns 543 * itself (this) so that the caller can keep a reference to it. 544 * 545 * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to 546 * allow multiple tasks to run in parallel on a pool of threads managed by 547 * AsyncTask, however you can also use your own {@link Executor} for custom 548 * behavior. 549 * 550 * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from 551 * a thread pool is generally <em>not</em> what one wants, because the order 552 * of their operation is not defined. For example, if these tasks are used 553 * to modify any state in common (such as writing a file due to a button click), 554 * there are no guarantees on the order of the modifications. 555 * Without careful work it is possible in rare cases for the newer version 556 * of the data to be over-written by an older one, leading to obscure data 557 * loss and stability issues. Such changes are best 558 * executed in serial; to guarantee such work is serialized regardless of 559 * platform version you can use this function with {@link #SERIAL_EXECUTOR}. 560 * 561 * <p>This method must be invoked on the looper thread. 562 * 563 * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a 564 * convenient process-wide thread pool for tasks that are loosely coupled. 565 * @param params The parameters of the task. 566 * 567 * @return This instance of AsyncTask. 568 * 569 * @throws IllegalStateException If {@link #getStatus()} returns either 570 * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or 571 * the current thread is not a looper thread. 572 */ 573 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, 574 Params... params) { 575 if (Looper.myLooper() == null) { 576 throw new IllegalStateException("AsyncTask can be only instanciated on a " 577 + "looper thread. The current thread is " + Thread.currentThread()); 578 } 579 580 if (mStatus != Status.PENDING) { 581 switch (mStatus) { 582 case RUNNING: 583 throw new IllegalStateException("Cannot execute task:" 584 + " the task is already running."); 585 case FINISHED: 586 throw new IllegalStateException("Cannot execute task:" 587 + " the task has already been executed " 588 + "(a task can be executed only once)"); 589 } 590 } 591 592 mStatus = Status.RUNNING; 593 594 onPreExecute(); 595 596 mWorker.mParams = params; 597 exec.execute(mFuture); 598 599 return this; 600 } 601 602 /** 603 * Convenience version of {@link #execute(Object...)} for use with 604 * a simple Runnable object. 605 */ 606 public static void execute(Runnable runnable) { 607 sDefaultExecutor.execute(runnable); 608 } 609 610 /** 611 * This method can be invoked from {@link #doInBackground} to 612 * publish updates on the looper thread while the background computation is 613 * still running. Each call to this method will trigger the execution of 614 * {@link #onProgressUpdate} on the looper thread. 615 * 616 * {@link #onProgressUpdate} will note be called if the task has been 617 * canceled. 618 * 619 * @param values The progress values to update the UI with. 620 * 621 * @see #onProgressUpdate 622 * @see #doInBackground 623 */ 624 protected final void publishProgress(Progress... values) { 625 if (!isCancelled()) { 626 mHandler.obtainMessage(MESSAGE_POST_PROGRESS, 627 new AsyncTaskResult<Progress>(this, values)).sendToTarget(); 628 } 629 } 630 631 private void finish(Result result) { 632 if (isCancelled()) { 633 onCancelled(result); 634 } else { 635 onPostExecute(result); 636 } 637 mStatus = Status.FINISHED; 638 } 639 640 private static class InternalHandler extends Handler { 641 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 642 @Override 643 public void handleMessage(Message msg) { 644 AsyncTaskResult result = (AsyncTaskResult) msg.obj; 645 switch (msg.what) { 646 case MESSAGE_POST_RESULT: 647 // There is only one result 648 result.mTask.finish(result.mData[0]); 649 break; 650 case MESSAGE_POST_PROGRESS: 651 result.mTask.onProgressUpdate(result.mData); 652 break; 653 } 654 } 655 } 656 657 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { 658 Params[] mParams; 659 } 660 661 @SuppressWarnings({"RawUseOfParameterizedType"}) 662 private static class AsyncTaskResult<Data> { 663 final AsyncTask mTask; 664 final Data[] mData; 665 666 AsyncTaskResult(AsyncTask task, Data... data) { 667 mTask = task; 668 mData = data; 669 } 670 } 671} 672