简介

AsyncTask 在Android API 3引入,是为了使UI线程能被正确和容易地使用。它允许你在后台进行一些操作,并且把结果带到UI线程中,而不用自己去操纵Thread或Handler。它被设计为一个围绕着Thread和Handler的辅助类,而不是一种通用的线程框架。在API文档中,建议你把它用于一些耗时比较短(大部分情况下只有几秒)的操作,如果你需要让线程长期运行,则强烈推荐使用java.util.concurrent里的API来处理。

如上所述,一个异步任务是在后台中进行计算,并把结果带到UI线程中。在这里,一个异步任务定义了三个泛型类型(ParamsProgressResult),以及四个步骤(onPreExecutedoInBackgroundonProgressUpdateonPostExecute)。

用法

在官方文档中,以下载文件为例,提供了示例代码,先定义一个AsyncTask的子类:

 private class DownloadFilesTask 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");
}
}

然后通过调用它的实例的execute(Params…)方法来执行。

 new DownloadFilesTask().execute(url1, url2, url3);

源码解析

下面来看一下这个类的代码,本文以android-23版本的代码为例。

成员变量

首先是这个类的成员变量。

    private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture; private volatile Status mStatus = Status.PENDING;//表示线程的执行状态 private final AtomicBoolean mCancelled = new AtomicBoolean();//表示是否被取消
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();//表示是否被调用

其中WorkerRunnable是对Callable的封装,包装了一个异步任务的参数。Status是定义的表示异步任务状态的枚举类型,共有三个状态,分别是PENDING(待绪),RUNNING(正在运行),FINISHED(已经完成)。

成员方法

再来看API中提供的所有成员方法:

我们从上面的示例代码中一个异步任务的执行开始,逐步跟踪它的执行过程。

    @MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
} @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(); mWorker.mParams = params;
exec.execute(mFuture); return this;
}

从上面的代码可以看出,一个异步任务不能被重复执行,否则将会抛异常。当执行它的时候,状态会被设为Status.RUNNING,同时会调用onPreExecute()方法,然后把传进来的参数赋值到所封装的worker中,再调用一个Executor的 execute方法。

onPreExecute是前面所述的一个异步任务的四个步骤之一,它是用在UI线程中的。所以我们要在UI线程中调用execute方法,才能使其在UI线程中被调用。

上面所用的Executor为sDefaultExecutor,我们来看一下它的定义:

    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>();
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);
}
}
}

可以看到,默认的Executor是串行处理后台任务的。然而,需要注意的是,AsyncTask在Android API 11之前曾经被设计为并行的,所以如果你在API 11之后需要并行地执行任务,可以直接调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params)。对此,谷歌在support库提供了封装这个操作的兼容类AsyncTaskCompat,因此你也可以直接调用AsyncTaskCompat.executeParallel(task, params)来使它能够被并行执行。

那么,一个异步任务是如何完成各个步骤的依次调用的?我们再回到它的构造方法来:

    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被Executor执行时,会执行其mWorker。它在调用的时候,先把mTaskInvoked设置为true表示已调用,设置线程优先级,然后才执行doInBackground()方法,并执行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;
}

如果在执行doInBackground过程中发生异常的话,而上面的代码可能就不会被执行,所以通过重写FutureTaskdone方法,在里面调用postResultIfNotInvoked,以确保任务执行完成的步骤被调用。

在前面的代码中,getHandler()返回的是一个InternalHandler单例,该类代码如下:

    private static class InternalHandler extends Handler {
public InternalHandler() {
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;
}
}
}

也就是消息被绑到主线程的消息队列了。同样,更新进度也是通过这种方法来把消息发送到主线程的消息队列中去执行:

    protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}

而在任务完成的方法中,先判断任务是否被取消,被取消的话则回调被取消的方法,否则回调onPostExecute,最终把任务的状态设为已结束。源码如下:

    private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}

以上就是AsyncTask对后台任务及UI线程之间的交互所做的封装。

AsyncTask还提供了取消任务以及等待任务执行的结果的几个API,它们原是FutureTask所提供的特性,所以也只是通过调用mFuture的对应方法,这里不再赘述。

总结

通过上面的代码可以总结出几条:

  • AsyncTask在不同的版本可能是串行执行的,也可能是并行执行的。
  • 如果要确保任务被并行执行,可以通过support库的AsyncTaskCompat来执行。
  • 一个AsyncTask只能被执行一次,不能被多次执行。
  • 取消一个AsyncTask,可以设置是否允许中断它。
  • 如果取消了一个AsyncTask,则不会再回调onProgressUpdate
  • 当一个任务完成时,如果它已经被取消,则回调onCancelled方法,否则回调onPostExecute方法。

Android源码解析——AsyncTask的更多相关文章

  1. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

  2. android源码解析(十七)-->Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  3. Android源码解析——LruCache

    我认为在写涉及到数据结构或算法的实现类的源码解析博客时,不应该急于讲它的使用或马上展开对源码的解析,而是要先交待一下这个数据结构或算法的资料,了解它的设计,再从它的设计出发去讲如何实现,最后从实现的角 ...

  4. Android源码解析——Toast

    简介 Toast是一种向用户快速展示少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源 ...

  5. Android 源码解析之AsyncTask

    AsyncTask相信大家都不陌生,它是为了简化异步请求.更新UI操作而诞生的.使用它不仅可以完成我们的网络耗时操作,而且还可以在完成耗时操作后直接的更新我们所需要的UI组件.这使得它在android ...

  6. Android 源码解析 之 setContentView

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41894125,本文出自:[张鸿洋的博客] 大家在平时的开发中,对于setCont ...

  7. Android源码解析——Handler、Looper与MessageQueue

    本文的目的是来分析下 Android 系统中以 Handler.Looper.MessageQueue 组成的异步消息处理机制,通过源码来了解整个消息处理流程的走向以及相关三者之间的关系 需要先了解以 ...

  8. 【Android源码解析】View.post()到底干了啥

    emmm,大伙都知道,子线程是不能进行 UI 操作的,或者很多场景下,一些操作需要延迟执行,这些都可以通过 Handler 来解决.但说实话,实在是太懒了,总感觉写 Handler 太麻烦了,一不小心 ...

  9. Android 源码解析:单例模式-通过容器实现单例模式-懒加载方式

    本文分析了 Android 系统服务通过容器实现单例,确保系统服务的全局唯一. 开发过 Android 的用户肯定都用过这句代码,主要作用是把布局文件 XML 加载到系统中,转换为 Android 的 ...

随机推荐

  1. Java调用SQL脚本执行的方案

    在Java中调用SQL脚本的方式有多种,在这里只记录一种自己常用的方式,个人觉得挺实用方便的. 运用ScriptRunner这个类. import org.apache.ibatis.io.Resou ...

  2. Web标准的简单理解 不同内核浏览器的差异以及浏览器渲染简介(转)

    Web标准是一系列标准的集合.这些标准大概分三方面:结构.表现和行为.结构化主要有HTML, XHTML和XML,表现主要有CSS,行为标准主要包括对象模型,如 W3C DOM.ECMAScript等 ...

  3. jacascript DOM节点——节点关系与操作

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 节点关系 DOM可以将任何HTML描绘成一个由多层节点构成的结构.每个节点都拥有各自的特点.数据和方法,也 ...

  4. Linux下内存问题检测神器:Valgrind

    在写大型C/C++工程时难免会发生内存泄漏现象,系统编程中一个重要的方面就是有效地处理与内存相关的问题.你的工作越接近系统,你就需要面对越多的内存问题.有时这些问题非常琐碎,而更多时候它会演变成一个调 ...

  5. webstorm 2018.1 激活码 2018.4.8日更新

    安装完成后,打开 WebStorm, 在打开的 License Activation 窗口中选择 License server. 在输入框输入网址即可: http://hb5.s.osidea.cc: ...

  6. ACE工具概述

    一:ACE简介 ADAPTIVE 通信环境(ACE,ADAPTIVE Communication Enviroment)是一种广泛的主机基础设施中间件,ACE可以免费获得,可以在http://ace. ...

  7. JS随机数不重复

    方法一 思路:首先创建一个1到3000的数组,每次取一个数,然后去除数组中取出的这个数, 这样就可以实现永不重复. var count=3000; var originalArray=new Arra ...

  8. vue-cli的使用

    1.安装node https://nodejs.org/en/download/ 2.webpack安装[我选全局安装] 全局安装 npm install --global webpack 本地安装 ...

  9. pyquery 的用法 --爬虫解析库

    如果你对Web有所涉及,如果你比较喜欢用CSS选择器,如果你对jQuery有所了解,那么这里有一个更适合你的解析库--pyquery. 接下来,我们就来感受一下pyquery的强大之处. 1. 准备工 ...

  10. CSS滚动条样式定制

    效果图如下 <!DOCTYPE html> <!-- saved from url=(0061)http://www.xuanfengge.com/demo/201311/scrol ...