Skip to content

Commit df61887

Browse files
Romain GuyAndroid (Google) Code Review
authored andcommitted
Merge "Check whether an AsyncTask is created/executed on a looper thread."
2 parents bd3f526 + 7498ccb commit df61887

File tree

1 file changed

+61
-28
lines changed

1 file changed

+61
-28
lines changed

core/java/android/os/AsyncTask.java

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@
3232
import java.util.concurrent.atomic.AtomicInteger;
3333

3434
/**
35-
* <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
36-
* perform background operations and publish results on the UI thread without
37-
* having to manipulate threads and/or handlers.</p>
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>
3839
*
3940
* <p>An asynchronous task is defined by a computation that runs on a background thread and
40-
* whose result is published on the UI thread. An asynchronous task is defined by 3 generic
41+
* whose result is published on a looper thread. An asynchronous task is defined by 3 generic
4142
* types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
4243
* and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
4344
* <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
@@ -101,7 +102,7 @@
101102
* <h2>The 4 steps</h2>
102103
* <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
103104
* <ol>
104-
* <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
105+
* <li>{@link #onPreExecute()}, invoked on the looper thread immediately after the task
105106
* is executed. This step is normally used to setup the task, for instance by
106107
* showing a progress bar in the user interface.</li>
107108
* <li>{@link #doInBackground}, invoked on the background thread
@@ -110,14 +111,14 @@
110111
* of the asynchronous task are passed to this step. The result of the computation must
111112
* be returned by this step and will be passed back to the last step. This step
112113
* can also use {@link #publishProgress} to publish one or more units
113-
* of progress. These values are published on the UI thread, in the
114+
* of progress. These values are published on the looper thread, in the
114115
* {@link #onProgressUpdate} step.</li>
115-
* <li>{@link #onProgressUpdate}, invoked on the UI thread after a
116+
* <li>{@link #onProgressUpdate}, invoked on the looper thread after a
116117
* call to {@link #publishProgress}. The timing of the execution is
117118
* undefined. This method is used to display any form of progress in the user
118119
* interface while the background computation is still executing. For instance,
119120
* it can be used to animate a progress bar or show logs in a text field.</li>
120-
* <li>{@link #onPostExecute}, invoked on the UI thread after the background
121+
* <li>{@link #onPostExecute}, invoked on the looper thread after the background
121122
* computation finishes. The result of the background computation is passed to
122123
* this step as a parameter.</li>
123124
* </ol>
@@ -135,8 +136,8 @@
135136
* <p>There are a few threading rules that must be followed for this class to
136137
* work properly:</p>
137138
* <ul>
138-
* <li>The task instance must be created on the UI thread.</li>
139-
* <li>{@link #execute} must be invoked on the UI thread.</li>
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>
140141
* <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
141142
* {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
142143
* <li>The task can be executed only once (an exception will be thrown if
@@ -152,6 +153,9 @@
152153
* <li>Set member fields in {@link #doInBackground}, and refer to them in
153154
* {@link #onProgressUpdate} and {@link #onPostExecute}.
154155
* </ul>
156+
*
157+
* @see Looper
158+
* @see Handler
155159
*/
156160
public abstract class AsyncTask<Params, Progress, Result> {
157161
private static final String LOG_TAG = "AsyncTask";
@@ -187,7 +191,13 @@ public Thread newThread(Runnable r) {
187191
private static final int MESSAGE_POST_RESULT = 0x1;
188192
private static final int MESSAGE_POST_PROGRESS = 0x2;
189193

190-
private static final InternalHandler sHandler = new InternalHandler();
194+
private static final ThreadLocal<InternalHandler> sHandler =
195+
new ThreadLocal<InternalHandler>() {
196+
@Override
197+
protected InternalHandler initialValue() {
198+
return new InternalHandler();
199+
}
200+
};
191201

192202
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
193203
private final WorkerRunnable<Params, Result> mWorker;
@@ -196,6 +206,7 @@ public Thread newThread(Runnable r) {
196206
private volatile Status mStatus = Status.PENDING;
197207

198208
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
209+
private final InternalHandler mHandler;
199210

200211
private static class SerialExecutor implements Executor {
201212
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
@@ -242,9 +253,8 @@ public enum Status {
242253
FINISHED,
243254
}
244255

245-
/** @hide Used to force static handler to be created. */
256+
/** @hide */
246257
public static void init() {
247-
sHandler.getLooper();
248258
}
249259

250260
/** @hide */
@@ -253,9 +263,20 @@ public static void setDefaultExecutor(Executor exec) {
253263
}
254264

255265
/**
256-
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
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
257271
*/
258272
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+
259280
mWorker = new WorkerRunnable<Params, Result>() {
260281
public Result call() throws Exception {
261282
mTaskInvoked.set(true);
@@ -295,7 +316,12 @@ private void postResultIfNotInvoked(Result result) {
295316
}
296317

297318
private Result postResult(Result result) {
319+
<<<<<<< HEAD
298320
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.
299325
new AsyncTaskResult<Result>(this, result));
300326
message.sendToTarget();
301327
return result;
@@ -316,7 +342,7 @@ public final Status getStatus() {
316342
* by the caller of this task.
317343
*
318344
* This method can call {@link #publishProgress} to publish updates
319-
* on the UI thread.
345+
* on the looper thread.
320346
*
321347
* @param params The parameters of the task.
322348
*
@@ -329,7 +355,7 @@ public final Status getStatus() {
329355
protected abstract Result doInBackground(Params... params);
330356

331357
/**
332-
* Runs on the UI thread before {@link #doInBackground}.
358+
* Runs on the looper thread before {@link #doInBackground}.
333359
*
334360
* @see #onPostExecute
335361
* @see #doInBackground
@@ -338,7 +364,7 @@ protected void onPreExecute() {
338364
}
339365

340366
/**
341-
* <p>Runs on the UI thread after {@link #doInBackground}. The
367+
* <p>Runs on the looper thread after {@link #doInBackground}. The
342368
* specified result is the value returned by {@link #doInBackground}.</p>
343369
*
344370
* <p>This method won't be invoked if the task was cancelled.</p>
@@ -354,7 +380,7 @@ protected void onPostExecute(Result result) {
354380
}
355381

356382
/**
357-
* Runs on the UI thread after {@link #publishProgress} is invoked.
383+
* Runs on the looper thread after {@link #publishProgress} is invoked.
358384
* The specified values are the values passed to {@link #publishProgress}.
359385
*
360386
* @param values The values indicating progress.
@@ -367,7 +393,7 @@ protected void onProgressUpdate(Progress... values) {
367393
}
368394

369395
/**
370-
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
396+
* <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and
371397
* {@link #doInBackground(Object[])} has finished.</p>
372398
*
373399
* <p>The default implementation simply invokes {@link #onCancelled()} and
@@ -390,7 +416,7 @@ protected void onCancelled(Result result) {
390416
* This method is invoked by the default implementation of
391417
* {@link #onCancelled(Object)}.</p>
392418
*
393-
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
419+
* <p>Runs on the looper thread after {@link #cancel(boolean)} is invoked and
394420
* {@link #doInBackground(Object[])} has finished.</p>
395421
*
396422
* @see #onCancelled(Object)
@@ -425,7 +451,7 @@ public final boolean isCancelled() {
425451
* an attempt to stop the task.</p>
426452
*
427453
* <p>Calling this method will result in {@link #onCancelled(Object)} being
428-
* invoked on the UI thread after {@link #doInBackground(Object[])}
454+
* invoked on the looper thread after {@link #doInBackground(Object[])}
429455
* returns. Calling this method guarantees that {@link #onPostExecute(Object)}
430456
* is never invoked. After invoking this method, you should check the
431457
* value returned by {@link #isCancelled()} periodically from
@@ -498,14 +524,15 @@ public final Result get(long timeout, TimeUnit unit) throws InterruptedException
498524
* with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on
499525
* its use.
500526
*
501-
* <p>This method must be invoked on the UI thread.
527+
* <p>This method must be invoked on the looper thread.
502528
*
503529
* @param params The parameters of the task.
504530
*
505531
* @return This instance of AsyncTask.
506532
*
507533
* @throws IllegalStateException If {@link #getStatus()} returns either
508-
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
534+
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or
535+
* the current thread is not a looper thread.
509536
*/
510537
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
511538
return executeOnExecutor(sDefaultExecutor, params);
@@ -531,7 +558,7 @@ public final AsyncTask<Params, Progress, Result> execute(Params... params) {
531558
* executed in serial; to guarantee such work is serialized regardless of
532559
* platform version you can use this function with {@link #SERIAL_EXECUTOR}.
533560
*
534-
* <p>This method must be invoked on the UI thread.
561+
* <p>This method must be invoked on the looper thread.
535562
*
536563
* @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
537564
* convenient process-wide thread pool for tasks that are loosely coupled.
@@ -540,10 +567,16 @@ public final AsyncTask<Params, Progress, Result> execute(Params... params) {
540567
* @return This instance of AsyncTask.
541568
*
542569
* @throws IllegalStateException If {@link #getStatus()} returns either
543-
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
570+
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED} or
571+
* the current thread is not a looper thread.
544572
*/
545573
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
546574
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+
547580
if (mStatus != Status.PENDING) {
548581
switch (mStatus) {
549582
case RUNNING:
@@ -576,9 +609,9 @@ public static void execute(Runnable runnable) {
576609

577610
/**
578611
* This method can be invoked from {@link #doInBackground} to
579-
* publish updates on the UI thread while the background computation is
612+
* publish updates on the looper thread while the background computation is
580613
* still running. Each call to this method will trigger the execution of
581-
* {@link #onProgressUpdate} on the UI thread.
614+
* {@link #onProgressUpdate} on the looper thread.
582615
*
583616
* {@link #onProgressUpdate} will note be called if the task has been
584617
* canceled.
@@ -590,7 +623,7 @@ public static void execute(Runnable runnable) {
590623
*/
591624
protected final void publishProgress(Progress... values) {
592625
if (!isCancelled()) {
593-
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
626+
mHandler.obtainMessage(MESSAGE_POST_PROGRESS,
594627
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
595628
}
596629
}

0 commit comments

Comments
 (0)