summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Joe Onorato <joeo@google.com> 2011-01-16 13:04:51 -0800
committer Joe Onorato <joeo@google.com> 2011-01-16 13:09:52 -0800
commit81de61bfddceba0eb77b3aacea317594b0f1de49 (patch)
treee466faea10fcf8bb143c9a17ce53b94311473a7a
parent772f5600ef720945b98054246982fd3b711830a9 (diff)
Change the default of how AsyncTask enqueues.
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
-rw-r--r--api/current.xml38
-rw-r--r--core/java/android/os/AsyncTask.java79
2 files changed, 111 insertions, 6 deletions
diff --git a/api/current.xml b/api/current.xml
index 5866d1fbcf33..228f12558434 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -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"
@@ -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"
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 9f7e31cd8edb..adc0a8b322b9 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -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;
@@ -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);
@@ -162,8 +162,17 @@ public abstract class AsyncTask<Params, Progress, Result> {
}
};
- 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;
@@ -177,6 +186,32 @@ public abstract class AsyncTask<Params, Progress, Result> {
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.
@@ -433,7 +468,11 @@ public abstract class AsyncTask<Params, Progress, Result> {
/**
* 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.
*
@@ -445,6 +484,26 @@ public abstract class AsyncTask<Params, Progress, Result> {
* {@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:
@@ -462,12 +521,20 @@ public abstract class AsyncTask<Params, Progress, Result> {
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
* still running. Each call to this method will trigger the execution of