首先抛出一个问题,如何在不用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源码前,请先了解以下概念
若不了解,请参考博文Java Executor。
下面就来具体分析一下AsyncTask的源码。
整个AsyncTask由3部分组成,任务FutureTask,线程池Executor,与UI线程关联的Handler,结构示意图如下:
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 (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);
}
}
};
}
可以看到在AsyncTask的构造方法中初始化了两个成员变量,mWorker是一个WorkerRunnable对象,mFuture是一个FutureTask对象。这些两个对象正代表了要执行的异步任务。FutureTask其实只是WorkerRunnable的包装类,真正在后台线程执行的方法正是WorkerRunnable中的call方法。最终放入线程池执行的就是mFuture这个对象。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
AsyncTask一开始就初始化了两个线程池SerialExecutor和THREAD_POOL_EXECUTOR。源码如下:
/** * 用于并行执行任务. */
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/** * 用于串行执行任务. */
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.execute(mActive);
}
}
}
SerialExecutor用于任务的排队,真正执行任务的还是THREAD_POOL_EXECUTOR。
THREAD_POOL_EXECUTOR的初始化参数如下:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
参数跟CPU数量有关,以四核CPU为例,那么该线程池有5个核心线程,最大线程数为9,线程闲置超时时间为1s,任务队列的容量为128。这说明当任务队列中任务数量为128即满时,无法再向线程池提交任务,所以当使用AsyncTask并使用默认线程池时存在这样一个缺陷。
注意:这几个配置线程池的参数在不同SDK版本中不一样。
AsyncTask提供了两个方法来执行异步任务。源码如下:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
由第一个方法可以看出默认情况下使用的是SerialExecutor。
第二个方法则可以使用自定义线程池。同时,可以看到当任务状态为RUNNING或FINISHED时,执行任务会抛出 IllegalStateException,所以AsyncTask对象只能被execute一次。
AsyncTask的状态有以下三种:
public enum Status {
/** * Indicates that the task has not been executed yet. */
PENDING,
/** * Indicates that the task is running. */
RUNNING,
/** * Indicates that {@link AsyncTask#onPostExecute} has finished. */
FINISHED,
}
当状态为PENDING时,才会正常执行。接着看executeOnExecutor方法。将mStatus设为RUNNING,表示正在执行。执行大家很熟悉的onPreExecute方法,此时还在UI线程,最后exec.execute(mFuture),将上一步创建的任务对象mFuture提交给线程池exec。
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
...
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
这样,mWorker中的call方法就会在线程中执行了。
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));
}
};
mTaskInvoked为一个AtomicBoolean对象,设为true表示call方法被调用
postResult(doInBackground(mParams)),这里终于看到了我们熟悉的doInBackground方法,此时正处于线程池中的执行,执行结果被postResult方法传递到了UI线程。
正如文章开头所述,将异步任务执行结果传递到UI线程使用了Handler。
private static final InternalHandler sHandler = new InternalHandler();
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;
}
}
}
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();
}
}
在InternalHandler处理MESSAGE_POST_RESULT消息时,调用了AsyncTask的finish方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
finish方法首先判断任务是否被Cancel,如果被Cancel,则执行onCancelled回调方法,反之,则执行我们熟悉的onPostExecute。最后将状态设置为FINISHED,防止再次执行该任务。
根据Handler消息机制可知,要想使onPostExecute方法在UI线程中执行,sHandler必须与UI线程Looper绑定,那么根据AsyncTask的源码分析,第一次使用AsyncTask必须在UI线程,不然在非UI线程new InternalHandler()则有可能报错,因为普通thread没有Looper,所以会抛出RuntimeException(
“Can’t create handler inside thread that has not called Looper.prepare()”)。那么AsyncTask是如何保证第一次使用Async是在UI线程的呢?请看ActivityThread类中的main方法,AsyncTask的init方法会被调用,目的就是为了在主线程加载AsyncTask类,从而是InternalHandler在UI线程中初始化,源代码如下:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
AsyncTask中的模板方法
protected abstract Result doInBackground(Params... params);
protected void onPreExecute() {}
@SuppressWarnings({"UnusedDeclaration"})
protected void onPostExecute(Result result) {}
@SuppressWarnings({"UnusedDeclaration"})
protected void onProgressUpdate(Progress... values) {}
@SuppressWarnings({"UnusedParameters"})
protected void onCancelled(Result result) {
onCancelled();
}
protected void onCancelled() {}
不同的线程池相当于不同的策略。
将系统中的服务分为Async和Sync两层,Async中的任务不能阻塞,相当于UI线程,Sync中的任务可以阻塞,相后台线程池。然后添加队列层实现Async层与Sync层的通信,即线程池使用的BlockingQueue<Runnable>。