一、前言
如果通过 Thread 执行耗时操作,那么在操作完成之后,我们可能需要更新 UI,这时通常的做法就是通过 Handler 投递一个消息给 UI 线程,然后更新 UI。这种方式对于整个过程的控制比较精细,但是也有缺点,例如:代码相对臃肿,在多个任务同时执行时,不易对线程进行精确控制。
为了简化操作,Android 1.5 提供了工具类 AsnycTask,不再需要编写任务线程和 Handler 实例即可完成相同的任务。它更重量级,更易于使用。
二、AsyncTask 的定义与核心方法
public abstract class AsyncTask<Params, Progress, Result> { }
3 种泛型类型分别表示参数类型、后台任务执行的进度类型、返回的结果类型。不是每个参数都一定需要,如果不需要某个参数,可以设置为 Void。
一个异步任务的执行一般包括以下几个步骤:
execute(Params... params):执行在 UI 线程。执行一个异步任务,在代码中调用此方法,触发异步任务的执行。
onPreExecute():执行在 UI 线程,在 execute(Params... params) 被调用后立即执行,一般用来在执行后台任务前对 UI 做一些标记。
doInBackground(Params... params):执行在子线程。在 onPreExecute() 完成后立即执行,用于执行较为耗时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用 publishProgress(Progress... values) 来更新进度信息。
onProgressUpdate(Progress... values):执行在 UI 线程,在调用 publishProgress(Progress... values) 时,此方法被执行,直接将进度信息更新到 UI 组件上。
onPostExecute(Result result):执行在 UI 线程。当后台操作结束时,此方法将会被调用,doInBackground 函数返回的计算结果将作为参数传递到此方法中,直接将结果显示到 UI 组件上。
在使用的时候,有几点需要注意:
异步任务的实例必须在 UI 线程中创建。
execute(Params... params) 方法必须在 UI 线程中使用。
不能在 doInBackground(Params... params) 中更改 UI 组件的信息。
一个任务实例只能执行一次,如果执行第二次将会抛出异常。
三、AsyncTask 的实现基本原理
AsyncTask 内部是怎么执行的呢?它执行的过程与我们使用 Handler 又有什么区别呢?
我们先来看看 AsyncTask 里面的几个核心方法:
//这是一个 abstract 方法,因此必须覆写
@WorkerThread
protected abstract Result doInBackground(Params... params);
//执行在 doInBackground 之前,并且执行在 UI 线程
@MainThread
protected void onPreExecute() {
}
//后台操作执行完成后会调用的方法,在此更新 UI
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
//在此更新进度,调用 publishProgress(Progress... values) 时,
//此方法被执行
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
//execute方法
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
* 执行任务,注意 execute 方法必须在 UI 线程中调用
* @param exec 执行任务的线程池
* @param params 参数
* @return AsyncTask 实例
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
//状态检测,只有在 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();
//UI 线程传递过来的参数
mWorker.mParams = params;
//交给线程池管理器进行调度,参数为 FutureTask 类型,
//构造 mFuture 时 mWorker 被传递了进去
exec.execute(mFuture);
//返回自身,使得调用者可以保持一个引用
return this;
}
- publishProgress(Progress... values) 是 final 修饰的,不能覆写,只能去调用,一般会在 doInBackground(Params... params) 中调用此方法来更新进度条。
1. Status 枚举类
可以看到有一个 Status 的枚举类和 getStatus() 方法,Status 枚举代表了 AsyncTask 的状态,Status 枚举类代码如下:
//初始状态,未执行状态
private volatile Status mStatus = Status.PENDING;
public enum Status {
//未执行状态
PENDING,
//执行中
RUNNING,
//执行完成
FINISHED,
}
//返回当前状态
public final Status getStatus() {
return mStatus;
}
这几个状态在 AsyncTask 一次生命周期中很多地方被用到,非常重要。在调用 execute 时会判断该任务的状态,如果是非 PENDING 状态则会抛出异常,因此一个 AsyncTask 实例只能运行一次,因为运行过后,AsyncTask 的状态就变成 FINISHED 状态。
2. sDefaultExecutor
关于 sDefaultExecutor,它是 ThreadPoolExecute 的实例,用于管理提交到 AsyncTask 的任务,代码如下:
//默认的任务调度是顺序执行
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//顺序执行的 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 执行
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
上述代码可以看出,sDefaultExecutor 只负责将异步任务分发给THREAD_POOL_EXECUTOR 线程池,因此,真正执行任务的地方是 THREAD_POOL_EXECUTOR。
THREAD_POOL_EXECUTOR 的构造代码如下:
//当前可用的 CPU 数量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心线程数
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//最大的线程数量
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//线程空闲时的存留时间
private static final int KEEP_ALIVE_SECONDS = 30;
//线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
//新建一个线程
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//线程队列
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
//线程池
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
更多有关线程池的介绍可以看这篇博文 Android 多线程探索(三)— 线程池。
3. mWorker 与 mFuture
mWorker 实际上是 AsyncTask 的一个抽象内部类的实现对象实例,它实现了 Callable<Result> 接口中的 call() 方法。
mFuture 实际上是 FutureTask 的实例,FutureTask 是一个可管理的异步任务,使得这些异步任务可以被更精确的控制。
它们的初始化都在 AsyncTask 的构造函数中,代码如下:
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//执行 doInBackground
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//调用 postResult 将结果投递给 UI 线程
postResult(result);
}
return result;
}
};
//在 mFuture 实例中,将会调用 mWorker 做后台任务,完成后会调用 done 方法
//这里将 mWorker 作为参数传递给了 mFuture 对象
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
//如果 postResult 没有被执行,
//那么执行 postResultIfNotInvoked(),
//这里判断的是 mTaskInvoked 成员变量
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
mWorker 的 call 函数中调用了 doInBackground 函数,并且最后通过 postReult 函数将结果投递出去。如果 postResult 没有被调用,那么最终在 mFuture 的 done 函数就会调用 postResult 函数分发结果。
4. execute 函数
对于 execute 函数,我们来分析一下它的执行流程:
//execute方法
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
* 执行任务,注意 execute 方法必须在 UI 线程中调用
* @param exec 执行任务的线程池
* @param params 参数
* @return AsyncTask 实例
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
......
mStatus = Status.RUNNING;
//执行任务前的准备处理
onPreExecute();
//UI 线程传递过来的参数
mWorker.mParams = params;
//交给线程池管理器进行调度,参数为 FutureTask 类型,
//构造 mFuture 时 mWorker 被传递了进去
exec.execute(mFuture);
//返回自身,使得调用者可以保持一个引用
return this;
}
exec.execute(mFuture) 最终会进入到 ThreadPoolExecutor 的 execute 函数,如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
忽略对工作线程、任务数量的判断,这段代码的主要功能是将异步任务 mFuture 加入将要执行的队列中,因此我们关注的函数是 addWorker():
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
...
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//这里又生成了一个 Worker 对象,将异步任务传递给 w
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
...
//将 w 添加到 workers 里,这是一个 HashSet 集合对象
workers.add(w);
...
if (workerAdded) {
//启动该异步任务,即启动 mFuture 任务
t.start();
workerStarted = true;
}
}
}
...
return workerStarted;
}
上面代码将 mFuture 实例传递到 Worker 中,并将该 Worker 对象添加到工作线程队列等待执行。mFuture 是 FutureTask 类型,构造的时候传递的参数是 mWorker:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//这里调用的是 callable 的 call 函数,
//即 mWorker 的 call 函数
result = c.call();
ran = true;
} catch (Throwable ex) {
//代码省略
}
if (ran)
set(result);
}
//代码省略
}
当启动 mFuture 时就会执行其中的 run 函数,实际上调用的是 callable(这里是 mWorker)的 call 函数,mWorker 的 call 函数又调用了 doInBackground 函数,此时线程真正启动了。获取到了 call 函数的结果之后,调用了 set(result) 函数:
protected void set(V v) {
...
finishCompletion();
}
}
private void finishCompletion() {
...
done();
callable = null; // to reduce footprint
}
可以看到,set(result) 方法中又调用了 done() 方法。如果任务顺利执行完成,就会执行 done 方法。我们之前覆写了 mFuture 的 done 方法,获取调用结果,然后通过 postResult 将结果投递给 UI 线程。
5. sHandler
sHandler 实际上是 AsyncTask 内部类 InternalHandler 的实例,InternalHandler 继承了 Handler,下面我们来分析一下它的代码:
private static InternalHandler sHandler;
//mHandler 是 sHandler 的引用,
//详细请看 AsyncTask 的构造方法
private final Handler mHandler;
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
private Handler getHandler() {
return mHandler;
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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
//调用 AsyncTask.finish 方法
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//调用 AsyncTask.onProgressUpdate 方法
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
在处理消息时,遇到 MESSAGE_POST_RESULT
,它会调用 AsyncTask.finish 方法,我们来看下它的定义:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
//调用 onPostExecute 显示结果
onPostExecute(result);
}
//改变状态为 FINISHED
mStatus = Status.FINISHED;
}
原来,finish() 方法是负责调用 onPostExecute(Result result) 方法并改变任务状态。onPostExecute 函数执行在 sHandler 的线程中,sHandler 关联的就是 UI 线程,因此 onPostExecute 函数就执行在 UI 线程上了。
如果被开发者覆写的 doInBackground(Params... params) 方法中调用了 publishProgress(Progress... values) 方法,则通过 InternalHandler 实例 sHandler 发送一条 MESSAGE_POST_PROGRESS
消息,更新进度,sHandler 处理消息时,onProgressUpdate(Progress... values) 方法将被调用。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
如果任务执行时遇到异常,mCancelled 会被设置为 false,这时 onCancelled(result) 函数会被调用:
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
onCancelled();
}
@MainThread
protected void onCancelled() {
}
可以看到,onCancelled() 的方法体为空,我们可以对它进行覆写。
6. AsyncTaskResult
postResult 方法构建消息时,包含了一个 AsyncTaskResult 类型的对象。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
然后在 sHandler 实例对象的 handleMessage(Message msg) 中获取该对象。
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
AsyncTaskResult 是 AsyncTask 的一个内部类,用来包装执行结果的一个类,我们来看看 AsyncTaskResult 的内部结构:
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
可以看到,AsyncTaskResult 封装了一个 AsyncTask 的实例和某种类型的数据集。
四、缺点:
1. 内存泄露问题
在 Activity 中使用非静态匿名内部 AsyncTask 类,由于 Java 内部类的特点,AsyncTask 内部类会持有外部类的隐式引用。由于 AsyncTask 的生命周期可能比 Activity 长,当 Activity 进行销毁 AsyncTask 还在执行时,由于 AsyncTask 持有 Activity 的引用,导致 Activity 对象无法回收,进而产生内存泄露。
2. 结果丢失
另一个问题就是因为屏幕旋转等原因造成 Activity 重新创建时 AsyncTask 数据丢失的问题。当 Activity 销毁并重新创建后,还在运行的 AsyncTask 会持有一个 Activity 的非法引用即之前的 Activity 实例。导致 onPostExecute() 没有任何作用。
3. 串行并行多版本不一致
1.6 之前为串行,1.6 到 2.3 为并行,3.0 之后又改回为串行,但是可以通过 executeOnExecutor(Executor)
实现并行处理任务。
五、总结
当我们调用 execute(Params... params) 方法后,execute 方法会调用 onPreExecute() 方法,然后由 ThreadPoolExecutor 实例 THREAD_POOL_EXECUTOR 执行一个 FutureTask 任务,这个过程中 doInBackground(Params... params) 将被调用。
如果被开发者覆写的 doInBackground(Params... params) 方法中调用了 publishProgress(Progress... values) 方法,则通过 InternalHandler 实例 sHandler 发送一条 MESSAGE_POST_PROGRESS 消息,更新进度,sHandler 处理消息时,onProgressUpdate(Progress... values) 方法将被调用;
如果遇到异常,sHandler 处理消息时,onCancelled() 将会被调用,onCancelled() 是一个空方法;
如果执行成功,sHandler 处理消息时会调用 onPostExecute(Result result) 方法让用户在 UI 线程处理结果。
这里有个简单示例 Android AsyncTask 简单示例。