AsyncTask可以实现异步工作,它将耗时操作放在一个工作线程中进行,然后将这个工作线程的结果发送给UI线程来进行UI更新操作,用起来非常的方便。
下面我们先来简单说说AsyncTask的用法吧?
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
下面简单看看它是如何工作的?
1、当执行了AsyncTask的execute函数,doInBackground()会在一个工作线程中自动的执行。
2、onPreExecute(), onPostExecute(), onProgressUpdate()会在UI线程中执行。
3、doInBackground()函数的返回值会发送到onPostExecute()函数,作为它的参数传递进去。
4、在doInBackground()函数中可以调用publishProgress()函数,它会触发UI线程中的onProgressUpdate()函数执行。
也是就是说,在使用过程中,我们将耗时的操作放在doInBackground()函数中,这个函数会运行在一个单独的工作线程中,这个函数有一个返回值,这个返回值会作为onPostExecute()的参数,这样onPostExecute()函数就可以获得doInBackground()的运行结果,在UI线程中来进行更新的操作,另外,如果我们在doInBackground()中希望实时的进行UI的更新操作,我们可以在doInBackground()函数中调用publishProgress()函数,这个函数会触发UI线程中的onProgressUpdate()的执行。
上面把这个类的用法基本都说明清楚了,下面我们需要通过源码来说说它的工作原理了。
public abstract class AsyncTask<Params, Progress, Result> {
protected abstract Result doInBackground(Params... params);
protected void onPreExecute() {
}
protected void onPostExecute(Result result) {
}
protected void onProgressUpdate(Progress... values) {
}
}
上面的代码把前面说的几个函数都展示出来了,基本都需要我们自己去实现,把自己的处理逻辑加进去。我们需要说明的一点就是AsyncTask<Params, Progress, Result>
这里面的三个参数Params, Progress, Result,从上面的代码中也可以看到Params对应的就是doInBackground参数类型,Progress为onProgressUpdate的参数类型,Result就是doInBackground的返回值类型以及onPostExecute的参数类型。
下面我们来看看AsyncTask的execute方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
上面执行调用了函数executeOnExecutor,它带有两个参数:一个是sDefaultExecutor,它是一个Executor,另一个是params,它是doInBackground函数需要传入的参数,也就是它会使用线程池来实现这个异步操作。
下面看看sDefaultExecutor这个参数的定义:
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.execute(mActive);
}
}
}
上面代码可以看到SerialExecutor的实现,重点是execute方法,当传入一个Runnable,可以理解为它会把Runable存放到了一个ArrayDeque里面,后面就是使用scheduleNext方法来遍历这个ArrayDeque,取出每一个Runnable,添加到一个THREAD_POOL_EXECUTOR线程池中。
我们来看看THREAD_POOL_EXECUTOR这个线程池的创建。
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
这样在线程池中就会调用Runnable的run方法。
接着上面的看,当调用AsyncTask的execute方法,这个方法里面会执行executeOnExecutor函数,我们来看看这个函数。
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是传进来的sDefaultExecutor对象
//这里实质调用的就是上面SerialExecutor类里面的execute方法
exec.execute(mFuture);
return this;
}
看上面的注释,上面调用的是SerialExecutor类里面的execute方法,这个我们在上面分析过了,其实就是将mFuture放到了一个线程池中,我们来看看mFuture是什么东西。
private final FutureTask<Result> mFuture;
private final WorkerRunnable<Params, Result> mWorker;
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);
}
}
};
}
从上面代码我们看到mFuture就是一个FutureTask对象,关于FutureTask的用法,可以参考Executor线程池的几种用法这篇文章,FutureTask的创建接收一个Callable对象,上面这个Callable就是WorkerRunnable,我们可以来看看这类。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
在线程池中最终会执行WorkerRunnable里面的call方法,关于为什么会调用,可以看看Executor线程池的几种用法这篇文章。
下面我们来把思路整理一下:
下面通过我们的使用过程来进行分析。
1、DownloadImageTask task = new DownloadImageTask()
我们使用AsyncTask的时候会首先创建一个AsyncTask对象,在构造函数里面主要做了一件事,那就是创建一个FutureTask对象,FutureTask会接收一个Callable对象,就是WorkerRunnable。
2、task.execute()
它里面会调用executeOnExecutor函数,在executeOnExecutor里面会调用SerialExecutor的execute方法,在这个方法里面会将上面创建的FutureTask对象传递进行,然后它首先会把这个FutureTask封到一个Runnable里面,放到ArrayDeque里面,接着会从ArrayDeque里面一个一个的取出Runnable放到一个线程池中,线程池会分配一个线程最终会执行到FutureTask里面的call方法。
下面我们来看看FutureTask里面的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));
}
};
1、它首先调用的是doInBackground方法,这个方法就是我们要实现的方法,从这里我们可以看到这个doInBackground运行在线程池的一个线程中,也就是一个单独的工作线程中。
2、运行完成后,会调用postResult来处理doInBackground返回的结果。我们来看看它的具体实现。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
从上面的代码可以看出,它把doInBackground返回的结果封装到一个Message消息中,通过sHandler发送出去。
下面我们来看看sHandler的具体代码。
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;
}
}
}
我们知道这个消息会发生到一个消息队列里面,在UI线程中有一个Looper它会不断的从消息队列获取消息,最终回调InternalHandler的handleMessage方法,也就是说handleMessage方法运行在UI线程,现在已经不是线程池的那个线程了,关于Handler和Looper,可以看看这篇文章Looper与Handler解析。
从这里我们看看AsyncTask里面有一个线程池,耗时操作doInBackground会运行在线程池的线程中,UI线程和线程池的线程是通过Handler来进行通信,当耗时操作doInBackground在线程池的线程中运行完成,通过Handler把最终的结果带回到UI线程中,在UI线程中,也就是handleMessage中会拿到这个结果,从上面代码我们可以看到这个结果是被封装过的,它里面包含有AsyncTask的实例对象,最终会运行它的finish方法,我们来看看这个finish方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
从上面我们就看到它最终执行了onPostExecute,也就是说最终带回结果之后,在UI线程中它会执行onPostExecute函数并且把这个结果作为参数传递进去。
从这里我们做几点总结:
1、doInBackground运行在线程池的线程中
2、onPostExecute运行在UI线程中
3、这两个线程是通过Handler来进行通信的
这样就是实现了异步操作了,上面我们提到publishProgress()方法,我们知道这个方法是在doInBackground中也就是线程池线程中运行的,主要是为了跟UI线程进行通信来更新UI,下面我们再来看看这个方法。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
从上面代码,就可以看出它也是通过Handler来进行通信,它通过handler来通过UI进行更新,从上面的handleMessage里面我们也可以看到它的处理逻辑,它调用了onProgressUpdate函数,这个函数需要由我们自己来实现,也就是说这个函数运行在UI线程。
从Executor线程池的几种用法这篇文章中,我们可以知道FutureTask任务是可以被取消的,我们耗时任务是封装成FutureTask放到线程池中运行的,所以我们也可以对其进行取消,这里说明一点,AsyncTask里面的任务是可以取消的。下面我们来看看它的cancel函数。
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
从这里我们就可以看到它确实调用的是FutureTask的取消来取消这个任务的。
上面的整个过程将的相对比较浅,主要为了说明AsyncTask的本质还是Thread+Handler。另外推荐比较好的两篇文章: