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