AsyncTask
AsyncTask 是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新 UI。从实现上来说, AsyncTask 封装了 Thread 和 Handler,通过 AsyncTask 可以更加方便地执行后台任务以及在主线程中访问 UI,但是 AsyncTask 并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask 是一个抽象的泛型类,它提供了 Params、Progress 和 Result 这三个泛型参数,其中 Params 表示参数的类型,Progress 表示后台任务的执行进度的类型,而 Result 则表示后台任务的返回结果的类型,如果 AsyncTask 确实不需要传递具体的参数,那么这三个泛型参数可以用 Void 来代替。AsyncTask 这个类的声明如下。
public abstract class AsyncTask<Params, Progress, Result>。
AsyncTask 提供了 4 个核心方法,他们的含义如下表示。
- onPreExecute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作。
- doInBackground(Params...params),在线程池中执行,此方法用于执行异步任务,params 参数表示异步任务的输入参数。在此方法中可以通过 publishProgress 方法来更新任务的进度,publishProgress 方法会调用 onProgressUpdate 方法。另外此方法需要返回计算结果给 onPostExecute 方法。
- onProgressUpdate(Progress...values),在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
- onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中 result 参数是后台任务的返回值,即 doInBackground 的返回值。
上面几个方法,onPreExecute 先执行,接着是 doInBackground,最后才是 onPostExecute。除了上述四个方法以外,AsyncTask 还提供了 onCancelled 方法,他同样在主线程中执行,当异步任务被取消时,onCancelled 方法会被调用,这个时候 onPostExecute 则不会被调用。下面提供一个典型的示例,如下表示。
private class DownloadFiledTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground (URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
//Escape early if cancel() is called
if (isCancelled())
break;
}
return totalSize;
}
protected void onProgressUpdate (Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute (Long result) {
showDialog("Downloaded" + result + "bytes");
}
}
在上面的代码中,实现了一个具体的 AsyncTask 类,这个类主要用于模拟文件的下载过程,它的输入参数类型为 URL,后台任务的进程参数为 Integer,而后台任务的返回结果为 Long 类型。注意到 doInBackground 和 onProgressUpdate 方法它们的参数中均包含...的字样,在 Java 中...表示参数的数量不定,它是一种数组形式的参数,...的概念和 C 语言中的...是一致的。当要执行上述下载任务时,可以通过如下方式来完成:
new DownloadFilesTask().execute(url1, url2, url3);
在 DownloadFilesTask 中,doInBackGround 用来执行具体的下载任务并通过 publishProgress 方法来更新下载的进度,同时还要判断下载任务是否被外界取消了。当下载任务完成后,doInBackground 会返回结果,即下载的总字节数。需要注意的是,doInBackground 是在线程池中执行的。onProgressUpdate 用于更新界面中的下载进度,它运行在主线程,当 publishProgress 被调用时,此方法就会别调用,当下载任务执行完成后,onPostExecute 方法就会被调用,它也是运行在主线程中,这个时候我们就可以在界面上做出一些提示,比如弹出一个对话框告知用户下载已经完成。
AsyncTask 在具体使用过程中也是有一些条件限制的,主要有如下几点:
- AsyncTask 的类必须在主线程中加载,这就意味着第一次访问 AsyncTask 必须发生在主线程,当然这个过程在 Android 4.1 及以上版本中已经被系统自动完成。在 Android 5.0 的源码中,可以查看 ActivityThread 的 main 方法,它会调用 AsyncTask 的init 方法,这就满足了 AsyncTask 的类必须在主线程中进行加载这个条件了。
- AsyncTask 的对象必须在主线程中创建
- execute 方法必须在 UI 线程调用
- 不要在程序中直接调用 onPreExecute、onPostExecute、doInBackground 和 onProgressUpdate 方法。
- 一个 AsyncTask 对象只能执行一次,即只能调用一次 execute 方法,否则会报运行时异常。
- 在 Android 1.6 以前,AsyncTask 是串行执行任务的,Android 1.6 的时候 AsyncTask 开始采用线程池处理并行任务,但是从 Android 3.0 开始,为了避免 AsyncTask 所带来的并发错误,AsyncTask 又采用一个线程来串行执行任务。尽管如此,在 Android 3.0 以及后续的版本中,我们仍然可以通过 AsyncTask 的 executeOnExecutor 方法来并行地执行任务。
AsyncTask 的工作原理
为了分析 AsyncTask 的工作原理,我们从它的 execute 方法开始分析,execute 方法又会调用 executeOnExecutor 方法,它们的实现如下所示。
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.");
break;
case FINISHED:
throw new IllegelStateException("Cannot execute task: the task has already been executed (a task can be executed only once)");
break;
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在上面的代码中,sDefaultExecutor 实际上是一个串行的线程池,一个进程中所有的 AsyncTask 全部在这个串行的线程池中排队执行,这个排队执行的过程后面会在进行分析。在 executeOnExecutor 方法中,AsyncTask 的 onPreExecute 方法最先执行,然后线程池开始执行。下面分析线程池的执行过程,如下所示。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
public synchronized void execute (final Runnable r) {
mTasks.offer (new Runnable() {
public void run () {
try {
r.run();
} finall {
scheduleNext();
}
}
});
if (mActive == null){
scheduleNext ();
}
}
protected synchronized void scheduleNext () {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
从 SerialExecutor 的实现可以分析 AsyncTask 的排队执行的过程。首先系统会把 AsyncTask 的 Param 参数封装为 FutureTask 对象,FutureTask 是一个并发类,在这里它充当了 Runnable 的作用。接着这个 FutureTask 对象插入到任务队列 mTask 中,如果这个时候没有正在活动的 AsyncTask 任务,那么就会调用 SerialExecutor 的 scheduleNext 方法来执行下一个 AsyncTask 任务。同时当一个 AsyncTask 任务执行完成后,AsyncTask 会继续执行其他任务直到所有的任务都被执行为止,从这一点可以看出,在默认情况下,AsyncTask 是串行执行的。
AsyncTask 中有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR) 和一个 Handler (InternalHandler),其中线程池 SerialExecutor 用于任务的排队,而线程池切换到主线程。在 AsyncTask 的构造方法中有如下这么一段代码,由于 FutureTask 的 run 方法会调用 mWorker 的 call 方法,因此 mWorker 的 call 方法最终会在线程池中执行。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call () throw Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResutl(doInBackground(mParams));
}
};
在 mWorker 的 call 方法中,首先将 mTaskInvoked 设为 true,表示当前任务已经被调用过了,然后执行 AsyncTask 的 doInBackground 方法,接着将其返回给 postResult 方法,它的实现如下所示。
private Result postResult (Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在上面的代码中,postResult 方法会通过 sHandler 发送一个 MESSAGE_POST_RESULT 的消息,这个 sHandler 的定义如下所示。
private static final InternalHandler = new InternalHandler ();
private static class InternalHandler extends Hanlder {
@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 是一个静态的 Handler 对象,为了能够将执行环境切换到主线程,这就要求 sHandler 这个对象必须在主线程中创建。由于静态成员在加载类的时候进行初始化,因此这就变相要求 AsyncTask 的类必须在主线程中加载,否则同一个进程中的 AsyncTask 都将无法正常工作。sHandler 收到 MESSAGE_POST_RESULT 这个消息后会调用 AsyncTask 的 finish 方法,如下所示。
private void finish (Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute (result);
}
mStatus = Status.FINISHED;
}
AsyncTask 的 finish 方法的逻辑比较简单,如果 AsyncTask 被取消执行了,那么就调用 onCancelled 方法,否则就会调用 onPostExecute 方法,可以看到 doInBackground 的返回结果会传递给 onPostExecute 方法,到这里 AsyncTask 的整个过程就分析完毕了。
注:此篇摘自 Android 开发艺术。
完~
喜欢有帮助的话: 双击、评论、转发,动一动你的小手让更多的人知道!关注 Android_YangKe