1. AsyncTask的实现原理简介
首先抛出一个问题,如何在不用AsyncTask的情况下实现异步下载一张图片并在一个ImageView中显示?
可以这么做,创建一个线程,在这个线程中下载图片,然后通过Handler消息机制在UI主线程中显示这张图片。大致代码如下:
public class MainActivity extends Activity {
private ImageView mImageView;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bitmap result = (Bitmap) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
mImageView.setImageBitmap(result);
break;
}
}
}
private void executeDownloadImageTask(Uri uri) {
new Thread(new Runnable() {
public void run() {
Bitmap bitmap = downloadImage(uri);
Message msg = Message.obtain();
msg.what = MESSAGE_POST_RESULT;
msg.obj = bitmap;
mHandler.sendMessage(msg);
}
}).start();
}
}
上述实现有一个缺陷,每次执行executeDownloadImageTask方法都会创建一个新的线程,当有大量的图片下载请求时,线程的数量就不可控,大量的线程之间可能因为互相抢占系统资源而导致阻塞,同时线程的创建和销毁都会带来性能开销。AsyncTask的实现与上述方法唯一不同的地方正是使用了线程池来管理线程。AsyncTask类中的方法onPostExecute(Result result)能在UI线程中处理结果,正是利用了Handler消息机制将执行doInBackground方法的返回结果传递到了UI线程。
在学习AsyncTask源码前,请先了解以下概念
- Callable<V>
- FutureTask<>
- Executor
- 线程池
若不了解,请参考博文Java Executor。
下面就来具体分析一下AsyncTask的源码。
2. AsyncTask源码分析
整个AsyncTask由3部分组成,任务FutureTask,线程池Executor,与UI线程关联的Handler,结构示意图如下:
2.1 创建任务
AsyncTask中的doInBackground方法会在非UI线程中执行,如何将其封装为一个任务类。源码如下:
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 (Interrupte