Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Change the default of how AsyncTask enqueues.
Browse files Browse the repository at this point in the history
The default is now to serialize everything onto the thread pool.  If you would
like to have finer grained control over how AsyncTasks are executed, you can
call the new executeOnExecutor method, which takes a custom Executor.  The
pool used by the default is handily now accessible as THREAD_POOL_EXECUTOR.

This change is because it is too tempting to convert single threaded Janky™
code to use AsyncTask in a way that is unsafe and ends up calling the
AsyncTasks in parallel.

Also, this adds a static execute(Runnable) method to AsyncTask that posts
onto the serialized queue, so that if you don't have any parameters or
return values, you can just use Runnable instead of AsyncTask.

Change-Id: I91bdfb00193542bfc2e101bcad56e3430421884a
  • Loading branch information
onoratoj committed Jan 16, 2011
1 parent 772f560 commit 81de61b
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 6 deletions.
38 changes: 38 additions & 0 deletions api/current.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137554,6 +137554,34 @@
<parameter name="params" type="Params...">
</parameter>
</method>
<method name="execute"
return="void"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="runnable" type="java.lang.Runnable">
</parameter>
</method>
<method name="executeOnExecutor"
return="android.os.AsyncTask&lt;Params, Progress, Result&gt;"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
<parameter name="exec" type="java.util.concurrent.Executor">
</parameter>
<parameter name="params" type="Params...">
</parameter>
</method>
<method name="get"
return="Result"
abstract="false"
Expand Down Expand Up @@ -137686,6 +137714,16 @@
<parameter name="values" type="Progress...">
</parameter>
</method>
<field name="THREAD_POOL_EXECUTOR"
type="java.util.concurrent.ThreadPoolExecutor"
transient="false"
volatile="false"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class>
<class name="AsyncTask.Status"
extends="java.lang.Enum"
Expand Down
79 changes: 73 additions & 6 deletions core/java/android/os/AsyncTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

package android.os;

import java.util.ArrayDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
Expand Down Expand Up @@ -151,8 +153,6 @@ public abstract class AsyncTask<Params, Progress, Result> {
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;

private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
Expand All @@ -162,8 +162,17 @@ public Thread newThread(Runnable r) {
}
};

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10);

/**
* A {@link ThreadPoolExecutor} that can be used to execute tasks in parallel.
*/
public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

private static final SerialExecutor sSerialExecutor = new SerialExecutor();

private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
Expand All @@ -177,6 +186,32 @@ public Thread newThread(Runnable r) {

private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}

protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}

/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
Expand Down Expand Up @@ -433,7 +468,11 @@ public final Result get(long timeout, TimeUnit unit) throws InterruptedException

/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
* itself (this) so that the caller can keep a reference to it. The tasks
* started by all invocations of this method in a given process are run
* sequentially. Call the {@link #execute(Executor,Params...) execute(Executor,Params...)}
* with a custom {@link Executor} to have finer grained control over how the
* tasks are run.
*
* This method must be invoked on the UI thread.
*
Expand All @@ -445,6 +484,26 @@ public final Result get(long timeout, TimeUnit unit) throws InterruptedException
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*/
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sSerialExecutor, params);
}

/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
* This method must be invoked on the UI thread.
*
* @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
* convenient process-wide thread pool for tasks that are loosely coupled.
* @param params The parameters of the task.
*
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*/
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
Expand All @@ -462,11 +521,19 @@ public final AsyncTask<Params, Progress, Result> execute(Params... params) {
onPreExecute();

mWorker.mParams = params;
sExecutor.execute(mFuture);
exec.execute(mFuture);

return this;
}

/**
* Schedules the {@link Runnable} in serial with the other AsyncTasks that were started
* with {@link #execute}.
*/
public static void execute(Runnable runnable) {
sSerialExecutor.execute(runnable);
}

/**
* This method can be invoked from {@link #doInBackground} to
* publish updates on the UI thread while the background computation is
Expand Down

0 comments on commit 81de61b

Please sign in to comment.