AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent
package such as Executor
, ThreadPoolExecutor
and FutureTask
.
Threading rules
There are a few threading rules that must be followed for this class to work properly:
- The AsyncTask class must be loaded on the UI thread. This is done automatically as of
JELLY_BEAN
. - The task instance must be created on the UI thread.
execute(Params...)
must be invoked on the UI thread.- Do not call
onPreExecute()
,onPostExecute(Result)
,doInBackground(Params...)
,onProgressUpdate(Progress...)
manually. - The task can be executed only once (an exception will be thrown if a second execution is attempted.)
Starting with DONUT
, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB
, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[])
with THREAD_POOL_EXECUTOR
.
在AsyncTask到构造方法中,初始化一个WorkerRunnable和一个FutureTask
<span style="font-size:18px;"> /**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}</span>
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在UI线程执行了onPreExecute(),
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);
}
}
}
也可以直接调用 executeOnExecutor,传递一个自定义到Executor,SerialExecutor实现了对任务顺序执行。
调用postResult(),发消息到UI线程执行onPostExecute()
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
AsyncTask的实例必须在UI线程初始化到原因是因为要让UI线程更新进度,后台任务执行结果传递给onPostExcute,该方法要在UI线程执行,所以初始化Handler时传给它到Looper必须是UI线程的。