AsyncTask.java revision 23fdaf6fb62a9b5154b2508916a21c678462c5d0
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>Threading rules</h2>
116 * <p>There are a few threading rules that must be followed for this class to
117 * work properly:</p>
118 * <ul>
119 *     <li>The task instance must be created on the UI thread.</li>
120 *     <li>{@link #execute} must be invoked on the UI thread.</li>
121 *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
122 *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
123 *     <li>The task can be executed only once (an exception will be thrown if
124 *     a second execution is attempted.)</li>
125 * </ul>
126 */
127public abstract class AsyncTask<Params, Progress, Result> {
128    private static final String LOG_TAG = "AsyncTask";
129
130    private static final int CORE_POOL_SIZE = 5;
131    private static final int MAXIMUM_POOL_SIZE = 128;
132    private static final int KEEP_ALIVE = 10;
133
134    private static final BlockingQueue<Runnable> sWorkQueue =
135            new LinkedBlockingQueue<Runnable>(10);
136
137    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
138        private final AtomicInteger mCount = new AtomicInteger(1);
139
140        public Thread newThread(Runnable r) {
141            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
142        }
143    };
144
145    private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
146            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
147
148    private static final int MESSAGE_POST_RESULT = 0x1;
149    private static final int MESSAGE_POST_PROGRESS = 0x2;
150    private static final int MESSAGE_POST_CANCEL = 0x3;
151
152    private static final InternalHandler sHandler = new InternalHandler();
153
154    private final WorkerRunnable<Params, Result> mWorker;
155    private final FutureTask<Result> mFuture;
156
157    private volatile Status mStatus = Status.PENDING;
158
159    /**
160     * Indicates the current status of the task. Each status will be set only once
161     * during the lifetime of a task.
162     */
163    public enum Status {
164        /**
165         * Indicates that the task has not been executed yet.
166         */
167        PENDING,
168        /**
169         * Indicates that the task is running.
170         */
171        RUNNING,
172        /**
173         * Indicates that {@link AsyncTask#onPostExecute} has finished.
174         */
175        FINISHED,
176    }
177
178    /** @hide Used to force static handler to be created. */
179    public static void init() {
180        sHandler.getLooper();
181    }
182
183    /**
184     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
185     */
186    public AsyncTask() {
187        mWorker = new WorkerRunnable<Params, Result>() {
188            public Result call() throws Exception {
189                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
190                return doInBackground(mParams);
191            }
192        };
193
194        mFuture = new FutureTask<Result>(mWorker) {
195
196            @Override
197            protected void set(Result v) {
198                super.set(v);
199                if (isCancelled()) {
200                    Message message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
201                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
202                    message.sendToTarget();
203                }
204            }
205
206            @Override
207            protected void done() {
208                Message message;
209                Result result = null;
210
211                try {
212                    result = get();
213                } catch (InterruptedException e) {
214                    android.util.Log.w(LOG_TAG, e);
215                } catch (ExecutionException e) {
216                    throw new RuntimeException("An error occured while executing doInBackground()",
217                            e.getCause());
218                } catch (CancellationException e) {
219                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
220                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
221                    message.sendToTarget();
222                    return;
223                } catch (Throwable t) {
224                    throw new RuntimeException("An error occured while executing "
225                            + "doInBackground()", t);
226                }
227
228                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
229                        new AsyncTaskResult<Result>(AsyncTask.this, result));
230                message.sendToTarget();
231            }
232        };
233    }
234
235    /**
236     * Returns the current status of this task.
237     *
238     * @return The current status.
239     */
240    public final Status getStatus() {
241        return mStatus;
242    }
243
244    /**
245     * Override this method to perform a computation on a background thread. The
246     * specified parameters are the parameters passed to {@link #execute}
247     * by the caller of this task.
248     *
249     * This method can call {@link #publishProgress} to publish updates
250     * on the UI thread.
251     *
252     * @param params The parameters of the task.
253     *
254     * @return A result, defined by the subclass of this task.
255     *
256     * @see #onPreExecute()
257     * @see #onPostExecute
258     * @see #publishProgress
259     */
260    protected abstract Result doInBackground(Params... params);
261
262    /**
263     * Runs on the UI thread before {@link #doInBackground}.
264     *
265     * @see #onPostExecute
266     * @see #doInBackground
267     */
268    protected void onPreExecute() {
269    }
270
271    /**
272     * Runs on the UI thread after {@link #doInBackground}. The
273     * specified result is the value returned by {@link #doInBackground}
274     * or null if the task was cancelled or an exception occured.
275     *
276     * @param result The result of the operation computed by {@link #doInBackground}.
277     *
278     * @see #onPreExecute
279     * @see #doInBackground
280     */
281    @SuppressWarnings({"UnusedDeclaration"})
282    protected void onPostExecute(Result result) {
283    }
284
285    /**
286     * Runs on the UI thread after {@link #publishProgress} is invoked.
287     * The specified values are the values passed to {@link #publishProgress}.
288     *
289     * @param values The values indicating progress.
290     *
291     * @see #publishProgress
292     * @see #doInBackground
293     */
294    @SuppressWarnings({"UnusedDeclaration"})
295    protected void onProgressUpdate(Progress... values) {
296    }
297
298    /**
299     * Runs on the UI thread after {@link #cancel(boolean)} is invoked.
300     *
301     * @see #cancel(boolean)
302     * @see #isCancelled()
303     */
304    protected void onCancelled() {
305    }
306
307    /**
308     * Returns <tt>true</tt> if this task was cancelled before it completed
309     * normally.
310     *
311     * @return <tt>true</tt> if task was cancelled before it completed
312     *
313     * @see #cancel(boolean)
314     */
315    public final boolean isCancelled() {
316        return mFuture.isCancelled();
317    }
318
319    /**
320     * Attempts to cancel execution of this task.  This attempt will
321     * fail if the task has already completed, already been cancelled,
322     * or could not be cancelled for some other reason. If successful,
323     * and this task has not started when <tt>cancel</tt> is called,
324     * this task should never run.  If the task has already started,
325     * then the <tt>mayInterruptIfRunning</tt> parameter determines
326     * whether the thread executing this task should be interrupted in
327     * an attempt to stop the task.
328     *
329     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
330     *        task should be interrupted; otherwise, in-progress tasks are allowed
331     *        to complete.
332     *
333     * @return <tt>false</tt> if the task could not be cancelled,
334     *         typically because it has already completed normally;
335     *         <tt>true</tt> otherwise
336     *
337     * @see #isCancelled()
338     * @see #onCancelled()
339     */
340    public final boolean cancel(boolean mayInterruptIfRunning) {
341        return mFuture.cancel(mayInterruptIfRunning);
342    }
343
344    /**
345     * Waits if necessary for the computation to complete, and then
346     * retrieves its result.
347     *
348     * @return The computed result.
349     *
350     * @throws CancellationException If the computation was cancelled.
351     * @throws ExecutionException If the computation threw an exception.
352     * @throws InterruptedException If the current thread was interrupted
353     *         while waiting.
354     */
355    public final Result get() throws InterruptedException, ExecutionException {
356        return mFuture.get();
357    }
358
359    /**
360     * Waits if necessary for at most the given time for the computation
361     * to complete, and then retrieves its result.
362     *
363     * @param timeout Time to wait before cancelling the operation.
364     * @param unit The time unit for the timeout.
365     *
366     * @return The computed result.
367     *
368     * @throws CancellationException If the computation was cancelled.
369     * @throws ExecutionException If the computation threw an exception.
370     * @throws InterruptedException If the current thread was interrupted
371     *         while waiting.
372     * @throws TimeoutException If the wait timed out.
373     */
374    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
375            ExecutionException, TimeoutException {
376        return mFuture.get(timeout, unit);
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     * This method must be invoked on the UI thread.
384     *
385     * @param params The parameters of the task.
386     *
387     * @return This instance of AsyncTask.
388     *
389     * @throws IllegalStateException If {@link #getStatus()} returns either
390     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
391     */
392    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
393        if (mStatus != Status.PENDING) {
394            switch (mStatus) {
395                case RUNNING:
396                    throw new IllegalStateException("Cannot execute task:"
397                            + " the task is already running.");
398                case FINISHED:
399                    throw new IllegalStateException("Cannot execute task:"
400                            + " the task has already been executed "
401                            + "(a task can be executed only once)");
402            }
403        }
404
405        mStatus = Status.RUNNING;
406
407        onPreExecute();
408
409        mWorker.mParams = params;
410        sExecutor.execute(mFuture);
411
412        return this;
413    }
414
415    /**
416     * This method can be invoked from {@link #doInBackground} to
417     * publish updates on the UI thread while the background computation is
418     * still running. Each call to this method will trigger the execution of
419     * {@link #onProgressUpdate} on the UI thread.
420     *
421     * {@link #onProgressUpdate} will note be called if the task has been
422     * canceled.
423     *
424     * @param values The progress values to update the UI with.
425     *
426     * @see #onProgressUpdate
427     * @see #doInBackground
428     */
429    protected final void publishProgress(Progress... values) {
430        if (!isCancelled()) {
431            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
432                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
433        }
434    }
435
436    private void finish(Result result) {
437        if (isCancelled()) result = null;
438        onPostExecute(result);
439        mStatus = Status.FINISHED;
440    }
441
442    private static class InternalHandler extends Handler {
443        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
444        @Override
445        public void handleMessage(Message msg) {
446            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
447            switch (msg.what) {
448                case MESSAGE_POST_RESULT:
449                    // There is only one result
450                    result.mTask.finish(result.mData[0]);
451                    break;
452                case MESSAGE_POST_PROGRESS:
453                    result.mTask.onProgressUpdate(result.mData);
454                    break;
455                case MESSAGE_POST_CANCEL:
456                    result.mTask.onCancelled();
457                    break;
458            }
459        }
460    }
461
462    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
463        Params[] mParams;
464    }
465
466    @SuppressWarnings({"RawUseOfParameterizedType"})
467    private static class AsyncTaskResult<Data> {
468        final AsyncTask mTask;
469        final Data[] mData;
470
471        AsyncTaskResult(AsyncTask task, Data... data) {
472            mTask = task;
473            mData = data;
474        }
475    }
476}
477