前言:本文章假定读者熟悉AsyncTask
的基本用法。
下面,我们从构造一个AsyncTask
开始讲起。构造函数中,它将初始化mWorker
和mFuture
,而mWorker
调用doInBackground
完成实际的工作。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(
Process.THREAD_PRIORITY_BACKGROUND);
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
执行成功的情况下,postResult(result)
最后会调用onPostExecute()
,将结果发布至主线程。 同时,该return
语句也将结果存放至 mFuture
中。
此外需要注意的一点是,这里将线程优先级设置为了background,以防止占用过多的资源。
至于mFuture
,主要用于执行任务的取消策略。具体的,可以看看AsyncTask.cancel()
方法
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
此外,AsyncTask.get()
也通过mFuture
,将结果返回给调用者。
默认情况下,讲在sDefaultExecutor
中执行对应的任务,可以直接调用executeOnExecutor
或setDefaultExecutor(executor)
方法,选择一种执行策略。
AsyncTask
提供两种Executor
的实现,分别是AsyncTask.THREAD_POOL_EXECUTOR
和AsyncTask.SERIAL_EXECUTOR
,sDefaultExecutor
默认情况下为SERIAL_EXECUTOR
。此外,由于这两个Executor
均是静态变量,整个应用共享一个线程池。
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);
}
}
}
THREAD_POOL_EXECUTOR
直接使用ThreadPoolExecutor
实现,而SERIAL_EXECUTOR
实际上也将工作交由THREAD_POOL_EXECUTOR
处理,只是在内部使用一个ArrayDeque
,以实现串行执行的功能。
此处需注意的,ArrayDeque.poll()
方法并不阻塞,失败时,它将返回null
,此时,也释放了mActive
所指向(refer to)的对象,防止了内存的泄露。
在doInBackground()
方法中,我们可以调用publishProgress()
将进度发布至主线程。进度(grogress)和结果(result)的发布,都通过内部的InternalHandler
类处理。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
这里调用Looper.getMainLooper()
以获取主线程的looper
,从而确保结果发布至主线程。
此外,察看文档,还可以发现一个经常被忽略的函数:
// AsyncTask
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
这里,我们可以将AsyncTask
当成一个方便的线程池,偶尔用来执行一些Runnable
(由于内部的线程池是公用的,不建议大量使用)。
还有一个我们直接查看源码可能无法发现的问题是,API level 11前,sDefaultExecutor 并非串行的。直接调用此方法,存在移植性问题。为此,我们可以使用support.v4包中的AsyncTaskCompat
public static <Params, Progress, Result>
AsyncTask<Params, Progress, Result> executeParallel(
AsyncTask<Params, Progress, Result> task,
Params... params) {
if (task == null) {
throw new IllegalArgumentException("task can not be null");
}
if (Build.VERSION.SDK_INT >= 11) {
// From API 11 onwards, we need to manually select
// the THREAD_POOL_EXECUTOR
AsyncTaskCompatHoneycomb.executeParallel(task, params);
} else {
// Before API 11, all tasks were run in parallel
task.execute(params);
}
return task;
}
AsyncTaskCompat
只有一个静态方法executeParallel()
,使用它,我们可以可移植地执行一个Runnable
。
基于此,如果应用需要明确的串行或并行执行,也应该显示调用executeOnExecutor()
。由于该线程池是全局的,以此目的调用setDefaultExecutor
并不是什么好主意。
另一点需要注意的是,THREAD_POOL_EXECUTOR
的队列容量为128:
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
总结:AsyncTask
总体上使用模板方法,将任务、取消、完成动作都交由子类实现,而执行策略则可以选择预定义的THREAD_POOL_EXECUTOR
和SERIAL_EXECUTOR
,也可以使用自己实现的Executor
或自定义ThreadPoolExecutor
。所以,执行策略还是非常灵活的。唯一的不足的没有(执行)失败策略,当然, 这也使得API更加的简单易用。
另外,在Executor中异步执行任务,而后通过Handler同步至主线程,也是Android平台的一个idiom,所谓的 half-sync, half-async。
文章中忽略了一些执行状态的细节,建议读者自己看看源代码,更细致地了解一下。