AsyncTask 源码分析
AsyncTask 是 Android 提供给我们的一个轻量级处理异步任务的类,可以把执行的进度和执行的结果反馈给 UI 线程来更新 UI。
- 文章来源:itsCoder 的 WeeklyBolg 项目
- itsCoder主页:http://itscoder.com/
- 作者:Melodyxxx
- 审阅者:Jaeger
AsyncTask的使用
AsyncTask的基本使用
Android 已经为我们将 AsyncTask 封装的已经很好了,所以 AsyncTask 使用起来也比较简单,下面是一般的使用方法:
new AsyncTask<String, Integer, Void>() {
// 运行在UI线程,异步任务执行前调用
@Override
protected void onPreExecute() {
}
// 运行在子线程,用于处理耗时操作
@Override
protected Void doInBackground(String... strings) {
// ...
// 触发onProgressUpdate方法更新进度信息
publishProgress(0);
return null;
}
// 运行在UI线程,可用于更新进度条进度信息
@Override
protected void onProgressUpdate(Integer... values) {
// update progress
}
// 运行在UI线程,异步任务执行完毕后调用
@Override
protected void onPostExecute(Void aVoid) {
}
}.execute("task_params");
AsyncTask的取消
可以手动调用 AsyncTask 的cancle()
方法来尝试取消一个任务,需要注意的是这里的取消只是改变任务的状态标记,并不会立马停止后台任务,可以通过isCancelled()
方法获取当前的取消状态来做相应的处理,例如可以在doInBackground()
方法里面像下面这样处理,不断的检测状态:
@Override
protected Void doInBackground(String... strings) {
for (int i = 0; i <= 10; i++){
// ...
if (isCancelled()){
break;
}
}
return null;
}
AsyncTask的工作原理分析
下面的分析的为Android 5.0的源码
先从 AsyncTask 从 AsyncTask 的执行入口方法execute()
开始分析
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
内部调用了 AsyncTas k的executeOnExecutor()
方法,跟进去
@MainThread
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()方法
onPreExecute();
// 将我们传进来的参数赋给mWorker.mParams
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
上面回调了onPreExecute()
方法,而当前线程为 AsyncTask 创建并启动的线程,所以AsyncTask 启动必须在 UI 线程,不能在子线程中,否则onPreExecute()
方法会在子线程中被调用。
在 AsyncTask 执行入口的 execute 方法中,将 sDefaultExecutor 传递给了 executeOnExecutor 的第一个参数,即上面的 exec ,这里的 sDefaultExecutor 是一个串行线程池,一个进程中的所有 AsyncTask 全部在这个串行线程池中排队执行:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
}
}
}
可以看到,这个线程池的 execute 方法会将任务进行排队,然后判断当前若没有正在执行的任务,则取出一个任务放进THREAD_POOL_EXECUTOR
线程池去执行,而这个THREAD_POOL_EXECUTOR
是一个并行线程池。所以又可以得出的结论是,AsyncTaks 内有两个线程池,一个串行线程池和一个并行线程池,串行线程池用于给任务排队使用,而THREAD_POOL_EXECUTOR
线程池才是任务真正执行的地方。
然后回到上面的executeOnExecutor()
方法内的exec.execute(mFuture)
这一行,由刚才分析可知这个 mFuture 就是我们的任务了,这个任务 mFuture 是在 AsyncTask 的构造器中初始化的:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
可以看到 mFuture 初始化的时候用到了 mWorker ,先记住 mWorker 里面有个call()
方法,再看 mFuture 的构造方法,可以看到,mFuture 将 mWorke r赋值给了 mFuture 内的 callable 对象:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
// mFuture将mWorker赋值给了mFuture内的callable对象
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
前面说了,AsyncTask 启动执行后,mFuture 会被放到THREAD_POOL_EXECUTOR
中去执行,执行会调用 mFuture 的run()
方法,所以下面看 mFuture 对应的的 FutureTask 中的run()
方法:
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
// 这里的c为之前的mWorker对象
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 调用mWorker的call()方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
所以执行 mFuture 后会调用 mWorker 的call()
方法,mWorker 的call()
方法实现如下:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
call()
方法内回调了熟悉的doInBackground()
方法,并将之前保存起来的 mWorker 内的 mParams 传给了它。需要注意的是,当前线程已经切换到了线程池中,而非 UI 线程中,所以doInBackground()
方法是在非 UI 线程中调用的。然后将doInBackground()
执行完的返回值 result 传给了postResult()
,跟进去看postResult()
的实现:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
这里使用 Handler 发送了一个MESSAGE_POST_RESULT
的消息,这里getHandler()
获取到的 Handler 是一个与 UI 主线程 Looper 相关联的 sHandler,下面是 sHandler 的获取和实例化过程。
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler() {
// 与UI主线程的Looper关联
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;
}
}
}
通过使用 sHandler 发送MESSAGE_POST_RESULT
消息,从线程池中切换到 UI 主线程中,然后执行 AsyncTask 的finish()
方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在 AsyncTask 的finish()
方法中首先会判断当前任务的状态是否已取消,如果已取消则会回调onCancelled()
方法,否则直接回调onPostExecute()
方法,最后将任务的状态设置为FINISHED
完成状态。
AsyncTask并行
在 Android 1.6 之前,AsyncTask 是串行执行任务的,Android 1.6 开始 AsyncTask 开始采用线程池处理并行任务,从 Android 3.0 开始,为了避免 AsyncTask 所带来的并发错误,AsyncTask 又采用一个线程来串行执行任务。
根据上面 Android 5.0 的源码分析可以看到 AsyncTask 是串行执行任务的,当然我们也可以手动使其并行执行任务,只要执行时把execute(params)
改成executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params)
即可,不过需要注意只能在 Android 3.0+ 才能这样做。
总结
- AsyncTask 使用起来比较方便,适合不是特别耗时的操作。
- AsyncTask 的 execute方法必须在主线程中调用。
- 再次启动一个已执行过的 AsyncTask 对象会抛异常。
- AsyncTask 在不同的 Android 版本上会有不同的表现。
如果还不了解线程池方面的文章,可以看看Joe的这个: ThreadPoolExecutor源码学习笔记
本文参考资料《Android开发艺术探索》