AsyncTask.java revision 5ba812b9e9c886425a5736c2ae6fbe0fc94afd8b
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.concurrent.BlockingQueue;
20import java.util.concurrent.Callable;
21import java.util.concurrent.CancellationException;
22import java.util.concurrent.ExecutionException;
23import java.util.concurrent.FutureTask;
24import java.util.concurrent.LinkedBlockingQueue;
25import java.util.concurrent.ThreadFactory;
26import java.util.concurrent.ThreadPoolExecutor;
27import java.util.concurrent.TimeUnit;
28import java.util.concurrent.TimeoutException;
29import java.util.concurrent.atomic.AtomicBoolean;
30import java.util.concurrent.atomic.AtomicInteger;
31
32/**
33 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
34 * perform background operations and publish results on the UI thread without
35 * having to manipulate threads and/or handlers.</p>
36 *
37 * <p>An asynchronous task is defined by a computation that runs on a background thread and
38 * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
39 * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
40 * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
41 * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
42 *
43 * <h2>Usage</h2>
44 * <p>AsyncTask must be subclassed to be used. The subclass will override at least
45 * one method ({@link #doInBackground}), and most often will override a
46 * second one ({@link #onPostExecute}.)</p>
47 *
48 * <p>Here is an example of subclassing:</p>
49 * <pre class="prettyprint">
50 * private class DownloadFilesTask extends AsyncTask&lt;URL, Integer, Long&gt; {
51 *     protected Long doInBackground(URL... urls) {
52 *         int count = urls.length;
53 *         long totalSize = 0;
54 *         for (int i = 0; i < count; i++) {
55 *             totalSize += Downloader.downloadFile(urls[i]);
56 *             publishProgress((int) ((i / (float) count) * 100));
57 *         }
58 *         return totalSize;
59 *     }
60 *
61 *     protected void onProgressUpdate(Integer... progress) {
62 *         setProgressPercent(progress[0]);
63 *     }
64 *
65 *     protected void onPostExecute(Long result) {
66 *         showDialog("Downloaded " + result + " bytes");
67 *     }
68 * }
69 * </pre>
70 *
71 * <p>Once created, a task is executed very simply:</p>
72 * <pre class="prettyprint">
73 * new DownloadFilesTask().execute(url1, url2, url3);
74 * </pre>
75 *
76 * <h2>AsyncTask's generic types</h2>
77 * <p>The three types used by an asynchronous task are the following:</p>
78 * <ol>
79 *     <li><code>Params</code>, the type of the parameters sent to the task upon
80 *     execution.</li>
81 *     <li><code>Progress</code>, the type of the progress units published during
82 *     the background computation.</li>
83 *     <li><code>Result</code>, the type of the result of the background
84 *     computation.</li>
85 * </ol>
86 * <p>Not all types are always used by an asynchronous task. To mark a type as unused,
87 * simply use the type {@link Void}:</p>
88 * <pre>
89 * private class MyTask extends AsyncTask&lt;Void, Void, Void&gt; { ... }
90 * </pre>
91 *
92 * <h2>The 4 steps</h2>
93 * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
94 * <ol>
95 *     <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
96 *     is executed. This step is normally used to setup the task, for instance by
97 *     showing a progress bar in the user interface.</li>
98 *     <li>{@link #doInBackground}, invoked on the background thread
99 *     immediately after {@link #onPreExecute()} finishes executing. This step is used
100 *     to perform background computation that can take a long time. The parameters
101 *     of the asynchronous task are passed to this step. The result of the computation must
102 *     be returned by this step and will be passed back to the last step. This step
103 *     can also use {@link #publishProgress} to publish one or more units
104 *     of progress. These values are published on the UI thread, in the
105 *     {@link #onProgressUpdate} step.</li>
106 *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
107 *     call to {@link #publishProgress}. The timing of the execution is
108 *     undefined. This method is used to display any form of progress in the user
109 *     interface while the background computation is still executing. For instance,
110 *     it can be used to animate a progress bar or show logs in a text field.</li>
111 *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
112 *     computation finishes. The result of the background computation is passed to
113 *     this step as a parameter.</li>
114 * </ol>
115 *
116 * <h2>Cancelling a task</h2>
117 * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
118 * this method will cause subsequent calls to {@link #isCancelled()} to return true.
119 * After invoking this method, {@link #onCancelled(Object)}, instead of
120 * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
121 * returns. To ensure that a task is cancelled as quickly as possible, you should always
122 * check the return value of {@link #isCancelled()} periodically from
123 * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
124 *
125 * <h2>Threading rules</h2>
126 * <p>There are a few threading rules that must be followed for this class to
127 * work properly:</p>
128 * <ul>
129 *     <li>The task instance must be created on the UI thread.</li>
130 *     <li>{@link #execute} must be invoked on the UI thread.</li>
131 *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
132 *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
133 *     <li>The task can be executed only once (an exception will be thrown if
134 *     a second execution is attempted.)</li>
135 * </ul>
136 *
137 * <h2>Memory observability</h2>
138 * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
139 * operations are safe without explicit synchronizations.</p>
140 * <ul>
141 *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
142 *     in {@link #doInBackground}.
143 *     <li>Set member fields in {@link #doInBackground}, and refer to them in
144 *     {@link #onProgressUpdate} and {@link #onPostExecute}.
145 * </ul>
146 */
147public abstract class AsyncTask<Params, Progress, Result> {
148    private static final String LOG_TAG = "AsyncTask";
149
150    private static final int CORE_POOL_SIZE = 5;
151    private static final int MAXIMUM_POOL_SIZE = 128;
152    private static final int KEEP_ALIVE = 1;
153
154    private static final BlockingQueue<Runnable> sWorkQueue =
155            new LinkedBlockingQueue<Runnable>(10);
156
157    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
158        private final AtomicInteger mCount = new AtomicInteger(1);
159
160        public Thread newThread(Runnable r) {
161            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
162        }
163    };
164
165    private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
166            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
167
168    private static final int MESSAGE_POST_RESULT = 0x1;
169    private static final int MESSAGE_POST_PROGRESS = 0x2;
170
171    private static final InternalHandler sHandler = new InternalHandler();
172
173    private final WorkerRunnable<Params, Result> mWorker;
174    private final FutureTask<Result> mFuture;
175
176    private volatile Status mStatus = Status.PENDING;
177
178    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
179
180    /**
181     * Indicates the current status of the task. Each status will be set only once
182     * during the lifetime of a task.
183     */
184    public enum Status {
185        /**
186         * Indicates that the task has not been executed yet.
187         */
188        PENDING,
189        /**
190         * Indicates that the task is running.
191         */
192        RUNNING,
193        /**
194         * Indicates that {@link AsyncTask#onPostExecute} has finished.
195         */
196        FINISHED,
197    }
198
199    /** @hide Used to force static handler to be created. */
200    public static void init() {
201        sHandler.getLooper();
202    }
203
204    /**
205     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
206     */
207    public AsyncTask() {
208        mWorker = new WorkerRunnable<Params, Result>() {
209            public Result call() throws Exception {
210                mTaskInvoked.set(true);
211
212                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
213                return postResult(doInBackground(mParams));
214            }
215        };
216
217        mFuture = new FutureTask<Result>(mWorker) {
218            @Override
219            protected void done() {
220                try {
221                    final Result result = get();
222
223                    postResultIfNotInvoked(result);
224                } catch (InterruptedException e) {
225                    android.util.Log.w(LOG_TAG, e);
226                } catch (ExecutionException e) {
227                    throw new RuntimeException("An error occured while executing doInBackground()",
228                            e.getCause());
229                } catch (CancellationException e) {
230                    postResultIfNotInvoked(null);
231                } catch (Throwable t) {
232                    throw new RuntimeException("An error occured while executing "
233                            + "doInBackground()", t);
234                }
235            }
236        };
237    }
238
239    private void postResultIfNotInvoked(Result result) {
240        final boolean wasTaskInvoked = mTaskInvoked.get();
241        if (!wasTaskInvoked) {
242            postResult(result);
243        }
244    }
245
246    private Result postResult(Result result) {
247        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
248                new AsyncTaskResult<Result>(this, result));
249        message.sendToTarget();
250        return result;
251    }
252
253    /**
254     * Returns the current status of this task.
255     *
256     * @return The current status.
257     */
258    public final Status getStatus() {
259        return mStatus;
260    }
261
262    /**
263     * Override this method to perform a computation on a background thread. The
264     * specified parameters are the parameters passed to {@link #execute}
265     * by the caller of this task.
266     *
267     * This method can call {@link #publishProgress} to publish updates
268     * on the UI thread.
269     *
270     * @param params The parameters of the task.
271     *
272     * @return A result, defined by the subclass of this task.
273     *
274     * @see #onPreExecute()
275     * @see #onPostExecute
276     * @see #publishProgress
277     */
278    protected abstract Result doInBackground(Params... params);
279
280    /**
281     * Runs on the UI thread before {@link #doInBackground}.
282     *
283     * @see #onPostExecute
284     * @see #doInBackground
285     */
286    protected void onPreExecute() {
287    }
288
289    /**
290     * <p>Runs on the UI thread after {@link #doInBackground}. The
291     * specified result is the value returned by {@link #doInBackground}.</p>
292     *
293     * <p>This method won't be invoked if the task was cancelled.</p>
294     *
295     * @param result The result of the operation computed by {@link #doInBackground}.
296     *
297     * @see #onPreExecute
298     * @see #doInBackground
299     * @see #onCancelled(Object)
300     */
301    @SuppressWarnings({"UnusedDeclaration"})
302    protected void onPostExecute(Result result) {
303    }
304
305    /**
306     * Runs on the UI thread after {@link #publishProgress} is invoked.
307     * The specified values are the values passed to {@link #publishProgress}.
308     *
309     * @param values The values indicating progress.
310     *
311     * @see #publishProgress
312     * @see #doInBackground
313     */
314    @SuppressWarnings({"UnusedDeclaration"})
315    protected void onProgressUpdate(Progress... values) {
316    }
317
318    /**
319     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
320     * {@link #doInBackground(Object[])} has finished.</p>
321     *
322     * <p>The default implementation simply invokes {@link #onCancelled()} and
323     * ignores the result. If you write your own implementation, do not call
324     * <code>super.onCancelled(result)</code>.</p>
325     *
326     * @param result The result, if any, computed in
327     *               {@link #doInBackground(Object[])}, can be null
328     *
329     * @see #cancel(boolean)
330     * @see #isCancelled()
331     */
332    @SuppressWarnings({"UnusedParameters"})
333    protected void onCancelled(Result result) {
334        onCancelled();
335    }
336
337    /**
338     * <p>Applications should preferably override {@link #onCancelled(Object)}.
339     * This method is invoked by the default implementation of
340     * {@link #onCancelled(Object)}.</p>
341     *
342     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
343     * {@link #doInBackground(Object[])} has finished.</p>
344     *
345     * @see #onCancelled(Object)
346     * @see #cancel(boolean)
347     * @see #isCancelled()
348     */
349    protected void onCancelled() {
350    }
351
352    /**
353     * Returns <tt>true</tt> if this task was cancelled before it completed
354     * normally. If you are calling {@link #cancel(boolean)} on the task,
355     * the value returned by this method should be checked periodically from
356     * {@link #doInBackground(Object[])} to end the task as soon as possible.
357     *
358     * @return <tt>true</tt> if task was cancelled before it completed
359     *
360     * @see #cancel(boolean)
361     */
362    public final boolean isCancelled() {
363        return mFuture.isCancelled();
364    }
365
366    /**
367     * <p>Attempts to cancel execution of this task.  This attempt will
368     * fail if the task has already completed, already been cancelled,
369     * or could not be cancelled for some other reason. If successful,
370     * and this task has not started when <tt>cancel</tt> is called,
371     * this task should never run. If the task has already started,
372     * then the <tt>mayInterruptIfRunning</tt> parameter determines
373     * whether the thread executing this task should be interrupted in
374     * an attempt to stop the task.</p>
375     *
376     * <p>Calling this method will result in {@link #onCancelled(Object)} being
377     * invoked on the UI thread after {@link #doInBackground(Object[])}
378     * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
379     * is never invoked. After invoking this method, you should check the
380     * value returned by {@link #isCancelled()} periodically from
381     * {@link #doInBackground(Object[])} to finish the task as early as
382     * possible.</p>
383     *
384     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
385     *        task should be interrupted; otherwise, in-progress tasks are allowed
386     *        to complete.
387     *
388     * @return <tt>false</tt> if the task could not be cancelled,
389     *         typically because it has already completed normally;
390     *         <tt>true</tt> otherwise
391     *
392     * @see #isCancelled()
393     * @see #onCancelled(Object)
394     */
395    public final boolean cancel(boolean mayInterruptIfRunning) {
396        return mFuture.cancel(mayInterruptIfRunning);
397    }
398
399    /**
400     * Waits if necessary for the computation to complete, and then
401     * retrieves its result.
402     *
403     * @return The computed result.
404     *
405     * @throws CancellationException If the computation was cancelled.
406     * @throws ExecutionException If the computation threw an exception.
407     * @throws InterruptedException If the current thread was interrupted
408     *         while waiting.
409     */
410    public final Result get() throws InterruptedException, ExecutionException {
411        return mFuture.get();
412    }
413
414    /**
415     * Waits if necessary for at most the given time for the computation
416     * to complete, and then retrieves its result.
417     *
418     * @param timeout Time to wait before cancelling the operation.
419     * @param unit The time unit for the timeout.
420     *
421     * @return The computed result.
422     *
423     * @throws CancellationException If the computation was cancelled.
424     * @throws ExecutionException If the computation threw an exception.
425     * @throws InterruptedException If the current thread was interrupted
426     *         while waiting.
427     * @throws TimeoutException If the wait timed out.
428     */
429    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
430            ExecutionException, TimeoutException {
431        return mFuture.get(timeout, unit);
432    }
433
434    /**
435     * Executes the task with the specified parameters. The task returns
436     * itself (this) so that the caller can keep a reference to it.
437     *
438     * This method must be invoked on the UI thread.
439     *
440     * @param params The parameters of the task.
441     *
442     * @return This instance of AsyncTask.
443     *
444     * @throws IllegalStateException If {@link #getStatus()} returns either
445     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
446     */
447    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
448        if (mStatus != Status.PENDING) {
449            switch (mStatus) {
450                case RUNNING:
451                    throw new IllegalStateException("Cannot execute task:"
452                            + " the task is already running.");
453                case FINISHED:
454                    throw new IllegalStateException("Cannot execute task:"
455                            + " the task has already been executed "
456                            + "(a task can be executed only once)");
457            }
458        }
459
460        mStatus = Status.RUNNING;
461
462        onPreExecute();
463
464        mWorker.mParams = params;
465        sExecutor.execute(mFuture);
466
467        return this;
468    }
469
470    /**
471     * This method can be invoked from {@link #doInBackground} to
472     * publish updates on the UI thread while the background computation is
473     * still running. Each call to this method will trigger the execution of
474     * {@link #onProgressUpdate} on the UI thread.
475     *
476     * {@link #onProgressUpdate} will note be called if the task has been
477     * canceled.
478     *
479     * @param values The progress values to update the UI with.
480     *
481     * @see #onProgressUpdate
482     * @see #doInBackground
483     */
484    protected final void publishProgress(Progress... values) {
485        if (!isCancelled()) {
486            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
487                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
488        }
489    }
490
491    private void finish(Result result) {
492        if (isCancelled()) {
493            onCancelled(result);
494        } else {
495            onPostExecute(result);
496        }
497        mStatus = Status.FINISHED;
498    }
499
500    private static class InternalHandler extends Handler {
501        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
502        @Override
503        public void handleMessage(Message msg) {
504            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
505            switch (msg.what) {
506                case MESSAGE_POST_RESULT:
507                    // There is only one result
508                    result.mTask.finish(result.mData[0]);
509                    break;
510                case MESSAGE_POST_PROGRESS:
511                    result.mTask.onProgressUpdate(result.mData);
512                    break;
513            }
514        }
515    }
516
517    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
518        Params[] mParams;
519    }
520
521    @SuppressWarnings({"RawUseOfParameterizedType"})
522    private static class AsyncTaskResult<Data> {
523        final AsyncTask mTask;
524        final Data[] mData;
525
526        AsyncTaskResult(AsyncTask task, Data... data) {
527            mTask = task;
528            mData = data;
529        }
530    }
531}
532