Android AsyncTask(线程间通信)

Android的AsyncTask用于在后台执行耗时操作,并在UI线程更新结果。它包含四个关键回调:onPreExecute、doInBackground、onProgressUpdate和onPostExecute。在API30中被废弃,因为可能导致内存泄漏和其他问题。AsyncTask内部使用线程池和Handler,通过execute方法启动,executeOnExecutor允许并行执行。在doInBackground中调用publishProgress更新进度,onProgressUpdate在主线程处理。当任务完成或取消时,onPostExecute或onCancelled在主线程执行。AsyncTask构造时初始化WorkerRunnable和FutureTask,执行过程中涉及线程切换和状态管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AsyncTask主要用于执行一些不太长的异步任务。作为替代Thread+Handler的辅助类,AsyncTask可以很轻松地执行异步任务并更新UI,但由于context泄露、回调遗漏、configuration变化导致崩溃、平台差异性等原因,在api30(Android 11)中,AsyncTask被正式废弃。

1.AsyncTask
AsyncTask类内部封装了Handler和线程池,方便在后台线程执行操作,将结果发送给主线程,然后在主线程中进行UI更新等操作。在使用AsyncTask时无需关注Thread和Handler,AsyncTask内部会对其进行管理,只需要关注业务逻辑即可。

AsyncTask有四个重要的回调方法,分别是:onPreExecute()、doInBackground()、onProgressUpdate()和onPostExecute()。这四个方法会在AsyncTask的不同时期进行自动调用,开发者只需要实现这几个方法的内部逻辑即可。这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress和Result:
在这里插入图片描述
①Params:AsyncTask执行任务的参数的类型。
②Progress:在后台线程处理的过程中,可以阶段性地发布结果的数据类型。
③Result:任务全部完成后所返回的数据类型。

通过调用AsyncTask的execute()方法传入参数并执行任务,然后AsyncTask会依次调用以下四个方法:
①onPreExecute
在这里插入图片描述
该方法运行在主线程。在AsyncTask执行了execute()方法后就会在UI线程上执行onPreExecute()方法,该方法在task真正执行前运行,通常可以在该方法中显示一个进度条,从而告知用户后台任务即将开始。
②doInBackground
在这里插入图片描述
该方法运行在单独的工作线程。doInBackground会在onPreExecute()方法执行完成后立即执行,用于在工作线程中执行耗时任务,可以在该方法中编写需要在后台线程中运行的逻辑代码。由于是运行在工作线程中,所以该方法不会阻塞UI线程。该方法接收Params泛型参数,参数params是Params类型的不定长数组,返回值是Result泛型,由于doInBackgroud是抽象方法,在使用AsyncTask时必须重写该方法。

在doInBackground中执行的任务可能要分解为好多步骤,每完成一步就可以通过调用AsyncTask的publishProgress(Progress…)将阶段性的处理结果发布出去,阶段性处理结果是Progress泛型类型。当调用了publishProgress方法后,处理结果会被传递到UI线程中,并在UI线程中回调onProgressUpdate方法。根据具体需要,可以在doInBackground中不调用publishProgress方法,或者多次调用publishProgress方法。

doInBackgroud方法的返回值表示后台线程完成任务之后的结果。

③onProgressUpdate
在doInBackground中调用publishProgress(Progress…)方法后,就会在UI线程上回调onProgressUpdate方法:
在这里插入图片描述
该方法在主线程上被调用,且传入的参数是Progress泛型定义的不定长数组。如果在doInBackground中多次调用了publishProgress方法,那么主线程就会多次回调onProgressUpdate方法。

④onPostExecute
该方法在主线程中被调用。当doInBackgroud方法执行完毕后就表示任务完成了,doInBackgroud方法的返回值就会作为参数在主线程中传入到onPostExecute方法中,这样就可以在主线程中根据任务的执行结果更新UI。

2.使用举例
以下载多个文件的示例演示AsyncTask的使用过程。
界面上有一个“开始下载”的按钮,点击该按钮即可通过AsyncTask下载多个文件,对应的Java代码:
public class MainActivity extends Activity {
TextView textView = null;
Button btnDownload = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(“iSpring”, "MainActivity -> onCreate, Thread name: " + Thread.currentThread().getName());
// 开始下载
btnDownload.setonClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String[] urls = {“http://”, “http://”,“http://”};
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute(urls);
}
});
}

private class DownloadTask extends AsyncTask<String, Object, Long> {
@Override
protected void onPreExecute() {
Log.i(“iSpring”, "DownloadTask -> onPreExecute, Thread name: " + Thread.currentThread().getName());
super.onPreExecute();
btnDownload.setEnabled(false);
textView.setText(“开始下载…”);
}

@Override
protected Long doInBackground(String… params) {
Log.i(“iSpring”, "DownloadTask -> doInBackground, Thread name: " + Thread.currentThread().getName());
long totalByte = 0;//所有下载的文件的总字节
//params是一个String数组
for(String url: params){
Object[] result = downloadSingleFile(url);
int byteCount = (int)result[0];
totalByte += byteCount;
//下载完一个文件之后,把阶段性的处理结果发布出去
publishProgress(result);
//如果AsyncTask被调用了cancel()方法,那么任务取消,跳出for循环
if(isCancelled()){
break;
}
}
//将总共下载的字节数作为结果返回
return totalByte;
}

//下载文件后返回一个Object数组:下载文件的字节数以及下载的博客的名字
private Object[] downloadSingleFile(String str){
Object[] result = new Object[2];
int byteCount = 0;
String blogName = “”;
HttpURLConnection conn = null;
try{
URL url = new URL(str);
conn = (HttpURLConnection) url.openConnection();
InputStream is = conn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
byteCount += length;
}
String respone = new String(baos.toByteArray(), “utf-8”);
int startIndex = respone.indexOf("< title>");
if(startIndex > 0){
startIndex += 7;
int endIndex = respone.indexOf("</ title>");
if(endIndex > startIndex){
//解析出博客中的标题
blogName = respone.substring(startIndex, endIndex);
}
}
}catch(MalformedURLException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally {
if(conn != null){
conn.disconnect();
}
}
result[0] = byteCount;
result[1] = blogName;
return result;
}

@Override
protected void onProgressUpdate(Object… values) {
Log.i(“iSpring”, "DownloadTask -> onProgressUpdate, Thread name: " + Thread.currentThread().getName());
super.onProgressUpdate(values);
int byteCount = (int)values[0];
String blogName = (String)values[1];
String text = textView.getText().toString();
text += “\n博客《” + blogName + “》下载完成,共” + byteCount + “字节”;
textView.setText(text);
}

@Override
protected void onPostExecute(Long aLong) {
Log.i(“iSpring”, "DownloadTask -> onPostExecute, Thread name: " + Thread.currentThread().getName());
super.onPostExecute(aLong);
String text = textView.getText().toString();
text += “\n全部下载完成,总共下载了” + aLong + “个字节”;
textView.setText(text);
btnDownload.setEnabled(true);
}

@Override
protected void onCancelled() {
Log.i(“iSpring”, "DownloadTask -> onCancelled, Thread name: " + Thread.currentThread().getName());
super.onCancelled();
textView.setText(“取消下载”);
btnDownload.setEnabled(true);
}
}
}
点击下载按钮后,界面如下所示:
在这里插入图片描述
打印log如下:
在这里插入图片描述
下面对以上代码进行一下说明。
①在doInBackground方法中,每当下载完一个文件,就会调用publishProgress方法发布阶段性结果,之后AsyncTask会回调onProgressUpdate方法,在此例中,onProgressUpdate的参数为Object类型,对应着AsyncTask中的Progress泛型类型。通过控制台输出可以发现,该方法是在主线程中调用的,在该方法中通过textView更新UI,告知用户哪个文件下载完成了。
②在整个doInBackground方法执行完毕后,AsyncTask就会回调onPostExecute方法,在该方法中再次通过textView更新UI告知用户全部下载任务完成了。
③在通过execute方法执行了异步任务之后,可以通过AsyncTask的cancel方法取消任务,取消任务后AsyncTask会回调onCancelled方法,这样不会再调用onPostExecute方法。

使用AsyncTask过程中,需要注意:
①AsyncTask的实例必须在主线程中创建,因为onPostExecute是AsyncTask的方法,AsyncTask若在子线程中运行,那么该方法也会在子线程中调用,就达不到刷新UI的效果了。
②AsyncTask的execute方法必须在主线程中调用,若在子线程中调用,那么在onPreExecute就会在子线程中运行,那么操作UI相关的操作就会出问题。
③对于一个AsyncTack的实例,只能执行一次execute方法,在该实例上第二次执行execute方法时就会抛出异常。

如果想并行地执行多个任务怎么办呢?可以考虑实例化多个AsyncTask实例,然后分别调用各个实例的execute方法,为了探究效果,将代码更改如下所示:
DownloadTask downloadTask1 = new DownloadTask();
downloadTask1.execute(urls);

DownloadTask downloadTask2 = new DownloadTask();
downloadTask2.execute(urls);
修改后,在单击了按钮之后,实例化了两个DownloadTask,并分别执行其execute方法,运行后界面如下所示:
在这里插入图片描述
打印log如下:
在这里插入图片描述
观察一下控制台的输出结果,可以发现对于downloadTask1,doInBackground方法是运行在线程“AsyncTask #1”中的;对于downloadTask2,doInBackground方法是运行在线程”AsyncTask #2”中的。但是,还可以发现,downloadTask1的doInBackground方法执行后,下载了五个文件,并五次触发了onProgressUpdate,在这之后才执行downloadTask2的doInBackground方法。对比两次的效果图也可以发现,在downloadTask1按照顺序下载完五篇文章之后,downloadTask2才开始按照顺序下载五篇文章。
综上所述,可以知道,默认情况下如果AsyncTask创建了多个实例,并同时执行实例的各个execute方法,那么这些实例的execute方法并不是并行执行的,而是串行执行的,即在第一个实例的doInBackground完成任务后,第二个实例的doInBackgroud方法才会开始执行,然后再执行第三个实例的doInBackground方法… 那么你可能会问,不对啊,上面downloadTask1是运行在”AsyncTask #1”线程中的,downloadTask2是运行在”AsyncTask #2”线程中的,这明明是两个线程啊!其实AsyncTask为downloadTask1开辟了名为”AsyncTask #1”的工作线程,在其完成了任务之后可能就销毁了,然后AsyncTask又为downloadTask2开辟了名为”AsyncTask #2”的工作线程。

AsyncTask在最早的版本中用一个单一的后台线程串行执行多个AsyncTask实例的任务,从Android 1.6(DONUT)开始,AsyncTask用线程池并行执行异步任务,但是从Android 3.0(HONEYCOMB)开始为了避免并行执行导致的常见错误,AsyncTask又开始默认用单线程作为工作线程处理多个任务。

从Android 3.0开始AsyncTask增加了executeOnExecutor方法,用该方法可以让AsyncTask并行处理任务:
public final AsyncTask<Params, Progress, Result> executeOnExecutor (Executor exec, Params… params)
第一个参数表示exec是一个Executor对象,为了让AsyncTask并行处理任务,通常情况下此处传入AsyncTask.THREAD_POOL_EXECUTOR即可,AsyncTask.THREAD_POOL_EXECUTOR是AsyncTask中内置的一个线程池对象,当然也可以传入自己实例化的线程池对象。第二个参数params表示要执行的任务的参数。
通过executeOnExecutor方法并行执行任务的示例代码如下所示:
DownloadTask downloadTask1 = new DownloadTask(); downloadTask1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urls);

DownloadTask downloadTask2 = new DownloadTask();
downloadTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urls);
实例化了两个DownloadTask的实例,然后执行了这两个实例的executeOnExecutor方法,并将AsyncTask.THREAD_POOL_EXECUTOR作为Executor传入,二者都接收同样的Url数组作为任务执行的参数。
点击下载按钮后,运行完的界面如下所示:
在这里插入图片描述
打印log如下:
在这里插入图片描述
可以看到,在downloadTask1执行了doInBackground方法后,downloadTask2也立即执行了doInBackground方法。并且通过程序运行完的UI界面可以看到在一个DownloadTask实例下载了一篇文章之后,另一个DownloadTask实例也立即下载了一篇文章,两个DownloadTask实例交叉按顺序下载文件,可以看出这两个AsyncTask的实例是并行执行的。

3.AsyncTask源码解析
(1)AsyncTask的线程池
AsyncTask类一开始定义了一个线程池:
public static final Executor THREAD_POOL_EXECUTOR;
static{
ThreadPoolExecutor ThreadPoolExecutor = new ThreadPoolExecutor(1, 20, 3, TimeUnit.SECONDS, new SynchronousQueue< Runnable>(), sThreadFactory);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
该线程池被定义为public static final,即AsyncTask内部维护了一个静态的线程池,默认情况下AsyncTask的实际工作就是通过该THREAD_POOL_EXECUTOR完成的。

然后AsyncTask里还有一个线程池:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
实例化了一个SerialExecutor类型的实例,也是public static final的。SerialExecutor是AsyncTask的一个内部类,它用来保证任务虽然可以异步提交,但是得到的是同步执行。
private static class SerialExecutor implements Executor {
final ArrayDeque< Runnable> mTasks = new ArrayDeque< Runnable>(); //任务列表
Runnable mActive; //当前正在执行的任务
public synchronized void execute(final Runnable r) { //同步方法,传入一个Runnable类型的对象
//实例化一个Runnable类型的匿名内部类对r进行封装,通过队列的offer方法将封装后的Runnable添加到队尾。这里不直接将传入的Runnable入队的原因是要在这控制任务的串行执行
mTasks.offer(new Runnable() {
public void run() {
try {
//执行r的run方法,开始执行任务。此处r的run方法是在线程池中执行的
r.run();
} finally {
//当任务执行完毕或抛出异常才会调用scheduleNext方法执行下一个Runnable任务
scheduleNext();
}
}
});
//只有当前没有执行任何任务时才会立即执行scheduleNext方法,真正调度任务的执行
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//通过队列的poll方法取出任务,poll()会删除并返回队头的Runnable,将返回的Runnable赋值给mActive,如果不为空就将其作为参数传递给THREAD_POOL_EXECUTOR的execute方法进行执行(mActive是出队的Runnable对象)
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute( mActive);
}
}
}
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
AsyncTask默认使用SERIAL_EXECUTOR作为Executor,即默认情况下AsyncTask是串行执行任务。
SerialExecutor会对提交的任务先进行入队,然后一个一个的从队列中取出,再交给线程池THREAD_POOL_EXECUTOR来执行。

总结一下:
①SerialExecutor实现了Executor接口中的execute方法,该类用于串行执行任务,即一个接一个地执行任务。
②SerialExecutor内部维护了一个存放Runnable的双端队列mTasks。当执行SerialExecutor的execute方法时会传入一个Runnable变量r,但是mTasks并不直接存储r,而是又新new了一个匿名Runnable对象,其内部会调用r,这样就对r进行了封装,将该封装后的Runnable对象通过队列的offer方法入队,添加到mTasks的队尾。
③SerialExecutor内部通过mActive存储着当前正在执行的任务Runnable。当执行SerialExecutor的execute方法时,首先会向mTasks的队尾添加一个Runnable,然后判断如果mActive为null,即当前没有任务正在运行,就会调用scheduleNext()方法。在scheduleNext方法里会首先从mTasks中通过poll方法取出队头任务,offer()方法会删除并返回队头的Runnable,将返回的Runnable赋值给mActive;如果mActive不为空就将其传递给THREAD_POOL_EXECUTOR的execute方法进行执行。所以SerialExecutor实际上是通过线程池THREAD_POOL_EXECUTOR进行实际的处理的。
④当将mTasks中的Runnable作为参数传递给THREAD_POOL_EXECUTOR执行execute方法时,会在线程池的工作线程中执行匿名内部类Runnable中的try-finally代码段,即先在工作线程中执行r.run()方法去执行任务,无论任务r正常完成还是抛出异常,都会在finally中执行scheduleNext方法用于执行mTasks中的下一个任务。此处可以看出SerialExecutor是一个接一个执行任务,即串行执行任务。
概括地说,如果有任务执行,那么SerialExecutor的execute方法会被调用,它的逻辑是把Runnable对象加入ArrayDeque队列中,然后判断mActive是否为空。第一次执行时mActive当然为空,所以执行scheduleNext,也就是取出任务队列中的第一个任务交给线程池THREAD_POOL_EXECUTOR执行。加入mTask队列的Runnable对象的run方法里最终一定会调用scheduleNext,又会从任务队列中取出队头任务执行。这样便实现了单线程顺序执行任务,所以在AsyncTask中默认启用的是单线程执行,只有上一个任务执行后才会执行下一个任务。

(2)AsyncTask的任务状态
AsyncTask内部定义了一个Status枚举类型:
public enum Status {
PENDING, //还没有开始执行任务
RUNNING, //已经开始执行任务
FINISHED, //任务已经执行完成或被取消,总之onPostExecute方法已经被调用了
}
一个AsyncTask正常情况下会经历PENDING->RUNNING->FINISHED三个状态。

AsyncTask的其他属性:
//InternalHandler是AsyncTask中定义的一个静态内部类,其绑定了主线程的Looper和消息队列
private static InternalHandler sHandler;
//mWorker是一个实现了Callable接口的对象,其实现了Callable接口的call方法
private final WorkerRunnable<Params, Result> mWorker;
//根据mFuture是一个FutureTask对象,需要用mWorker作为参数实例化mFuture
private final FutureTask< Result> mFuture;
//AsyncTask的初始状态位PENDING
private volatile Status mStatus = Status.PENDING;
//标识当前任务是否被取消了
private final AtomicBoolean mCancelled = new AtomicBoolean();
//标识当前任务是否真正开始执行了
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

InternalHandler是AsyncTask中定义的一个静态内部类:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
//用主线程的Looper初始化InternalHandler ,即InternalHandler绑定了主线程
super(looper);
}
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
//发布最后结果
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//发布阶段性处理结果
result.mTask.onProgressUpdate( result.mData);
break;
}
}
}
用主线程的Looper初始化了InternalHandler,说明InternalHandler绑定了主线程。

(3)AsyncTask中的FutureTask和Callable
由于AsyncTask能够取消任务,所以AsyncTask使用了FutureTask以及与其相关的Callable。FutureTask、Callable在并发编程中可以用来获取任务执行完之后的返回值,也可以取消线程池中的某个任务。
Callable是一个接口,其内部定义了call方法,在call方法内需要编写代码执行具体的任务,在这一点上Callable接口与Runnable接口很类似,不过不同的是Runnable的run方法没有返回值,而Callable的call方法可以指定返回值。
FutureTask类同时实现了Callable接口和Runnable接口,FutureTask的构造函数中需要传入一个Callable对象以对其进行实例化。
Executor的execute方法接收一个Runnable对象,由于FutureTask实现了Runnable接口,所以可以把一个FutureTask对象传递给Executor的execute方法去执行。当任务执行完毕的时候会执行FutureTask的done方法,可以在这个方法中写一些逻辑处理。在任务执行的过程中,也可以随时调用FutureTask的cancel方法取消执行任务,任务取消后也会执行FutureTask的done方法。也可以通过FutureTask的get方法阻塞式地等待任务的返回值(即Callable的call方法的返回值),如果任务执行完了就立即返回执行的结果,否则就阻塞式等待call方法的完成。

mFuture是FutureTask类型的对象,mWorker是WorkerRunnable类型的对象,WorkerRunnable是AsyncTask中的一个内部类:
private static abstract class WorkerRunnable< Params, Result> implements Callable< Result> {
Params[] mParams;
}
WorkerRunnable其实就是一个指定了泛型Result的Callable,所以mWorker也就是一个Callable对象。

(4)AsyncTask的构造函数
public AsyncTask() {
this((Looper)null);
}
public AsyncTask(Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper = = Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() { //call方法是在线程池的某个线程中执行的,而不是运行在主线程中
mTaskInvoked.set(true); //表示任务开始执行
//将执行call方法的线程设置为后台线程级别
Process.setThreadPriority( Process.THREAD_PRIORITY_BACKGROUND);
//在线程池的工作线程中调用doInBackground方法执行实际的任务并返回结果
Result result = doInBackground( mParams);
Binder.flushPendingCommands();
//将执行完的结果传递给postResult方法
return postResult(result);
}
};
//用mWorker实例化mFuture
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);
}
}
};
}
在AsyncTask的构造函数中依次实例化了mWorker和mFuture,AsyncTask的构造函数需要在UI线程上调用。下面对以上代码进行一下说明。
①mWorker
mWorker是一个Callable类型的对象。实例化mWorker实现了Callable接口的call方法。call方法是在线程池的某个线程中执行的,而不是运行在主线程中。在线程池的工作线程中调用doInBackground方法执行实际的任务,并返回结果。当doInBackground执行完毕后,将执行完的结果传递给postResult方法。
②mFuture
mFuture是一个FutureTask类型的对象,用mWorker作为参数实例化了mFuture。在这里,其实现了FutureTask的done方法,之前提到,当FutureTask的任务执行完成或任务取消的时候会执行FutureTask的done方法。

在实例化了AsyncTask对象之后,就可以调用AsyncTask的execute方法执行任务:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
return executeOnExecutor( sDefaultExecutor, params);
}
execute方法有@MainThread,所以该方法是在主线程上执行。它调用了executeOnExecutor方法,并把sDefaultExecutor作为参数传过去,此处就能看出sDefaultExecutor是默认执行任务的Executor了。

再看一下executeOnExecutor方法:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params… params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
//如果当前AsyncTask已经处于运行状态,就抛出异常,不再执行新的任务
throw new IllegalStateException( “Cannot execute task , the task is already running.”);
case FINISHED:
//如果当前AsyncTask已经把之前的任务运行完成,也抛出异常,不再执行新的任务
throw new IllegalStateException( “Cannot execute task: the task has already been executed (a task can be executed only once)”);
}
}
//将AsyncTask的状态置为运行状态
mStatus = Status.RUNNING;
//真正执行任务前,先调用onPreExecute方法
onPreExecute();

mWorker.mParams = params;
//Executor的execute方法接收Runnable参数,由于mFuture是FutureTask的实例,且FutureTask同时实现了Callable和Runnable接口,所以此处可以让exec执行mFuture
exec.execute(mFuture);

//最后将AsyncTask自身返回
return this;
}
对以上代码进行一下说明:
①一个AsyncTask实例只能执行一次任务,当第二次执行任务时就会抛出异常。executeOnExecutor方法一开始就检查AsyncTask的状态是不是PENDING,只有PENDING状态才往下执行,如果是其他状态表明现在正在执行另一个已有的任务或者已经执行完成了一个任务,这种情况下都会抛出异常。
②如果开始是PENDING状态就说明该AsyncTask还没执行过任何任务,代码可以继续执行,然后将状态设置为RUNNING,表示开始执行任务。
③在真正执行任务前,先调用onPreExecute方法。由于executeOnExecutor方法应该运行在主线程上,所以此处的onPreExecute方法也会运行在主线程上,可以在该方法中做一些UI上的处理操作。
④Executor的execute方法接收Runnable参数,由于mFuture是FutureTask的实例,且FutureTask同时实现了Callable和Runnable接口,所以此处可以让exec通过execute方法执行mFuture。在执行了exec.execute(mFuture)之后,后面会在exec的工作线程中执行mWorker的call方法,在mWorker的call方法内部的执行过程中会首先在工作线程中执行doInBackground方法并返回结果,然后将结果传递给postResult方法。

下面看一下postResult方法:
private Result postResult(Result result) {
//通过getHandler()获取InternalHandler,然后创建MESSAGE_POST_RESULT的Message
Message message = getHandler().obtainMe ssage(MESSAGE_POST_RESULT, new AsyncTaskResult< Result>(this, result));
//将该message发送给InternalHandler
message.sendToTarget();
return result;
}
在postResult方法中,通过getHandler获取InternalHandler,InternalHandler绑定主线程。getHandler()方法:
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
InternalHandler对象后会根据InternalHandler创建一个MESSAGE_POST_RESULT的Message,此处还将doInBackground返回的result通过new AsyncTaskResult< Result>(this, result)封装成了AsyncTaskResult,将其作为message的obj属性。

AsyncTaskResult是AsyncTask的一个内部类:
//AsyncTaskResult用于发布result或用于发布阶段性的数据
private static class AsyncTaskResult< Data> {
//当前AsyncTaskResult是哪个AsyncTask的结果
final AsyncTask mTask;
//存储的数据
final Data[] mData;

AsyncTaskResult(AsyncTask task, Data… data){
//此处的data是可变数组
mTask = task;
mData = data;
}
}
构建Message对象后通过message.sendToTarget()将该message发送给InternalHandler,之后InternalHandler的handleMessage方法会接收并处理该message:
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate( result.mData);
break;
}
}
msg.obj是AsyncTaskResult类型,result.mTask表示当前AsyncTaskResult所绑定的AsyncTask。result.mData[0]表示的是doInBackground所返回的处理结果。将该结果传递给AsyncTask的finish方法:
private void finish(Result result) {
if (isCancelled()) {
//如果任务被取消了执行onCancelled方法
onCancelled(result);
} else {
//将结果发传递给onPostExecute方法
onPostExecute(result);
}
//最后将AsyncTask的状态设置为完成状态
mStatus = Status.FINISHED;
}
finish方法内部会首先判断AsyncTask是否被取消了,如果被取消了执行onCancelled(result),否则执行onPostExecute(result)方法。需要注意的是InternalHandler是指向主线程的,所以其handleMessage方法是在主线程中执行的,从而此处的finish方法也是在主线程中执行的,进而onPostExecute也是在主线程中执行的。

现在知道了任务从开始执行到onPostExecute的过程。由于doInBackground方法是在工作线程中执行比较耗时的操作,这个操作时间可能比较长,而一般任务有可能分成多个部分,每当完成其中的一部分任务时,可以在doInBackground中多次调用AsyncTask的publishProgress方法,将阶段性数据发布出去。
publishProgress方法:
@WorkerThread
protected final void publishProgress( Progress… values) {
if (!isCancelled()) {
getHandler().obtainMessage( MESSAGE_POST_PROGRESS, new AsyncTaskResult< Progress>(this, values)).sendToTarget();
}
}
需要将阶段性处理的结果传入publishProgress,其中values是不定长数组,如果任务没有被取消,就会通过InternalHandler创建一个Message对象,该message的Message Code标记为MESSAGE_POST_PROGRESS,并且会根据传入的values创建AsyncTaskResult,将其作为message的obj属性。然后再将该message发送给InternalHandler,然后InternalHandler会执行handleMessage方法,接收并处理该message,如下所示:
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {

case MESSAGE_POST_PROGRESS:
//发布阶段性处理结果
result.mTask.onProgressUpdate( result.mData);
break;
}
}
在handleMessage方法中,当message.what为MESSAGE_POST_PROGRESS时,会执行AsyncTask的onProgressUpdate方法,该方法是在主线程中执行的。

AsyncTask无论任务完成还是取消任务,FutureTask都会执行done方法:
mFuture = new FutureTask< Result>(mWorker) {
@Override
protected void done() {
//任务执行完毕或取消任务都会执行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);
}
}
};
无论任务正常执行完成还是任务取消,都会执行postResultIfNotInvoked方法:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
//只有mWorker的call没有被调用才会执行postResult方法
postResult(result);
}
}
如果AsyncTask正常执行完成的时候,call方法都执行完了,mTaskInvoked设置为true,并且在call方法中最后执行了postResult方法,然后进入mFuture的done方法,然后进入postResultIfNotInvoked方法,由于mTaskInvoked已经执行,所以不会执行再执行postResult方法。

如果在调用了AsyncTask的execute方法后立马就执行了AsyncTask的cancel方法(实际执行mFuture的cancel方法),那么会执行done方法,且捕获到CancellationException异常,从而执行语句postResultIfNotInvoked(null),由于此时还没有来得及执行mWorker的call方法,所以mTaskInvoked还未false,这样就可以把null传递给postResult方法。

总结一下:
AsyncTask内部维护了一个线程池负责实例任务的执行,用Callable和FutureTask实现任务执行的可取消和有返回值。线程的调用,用系统默认实现的串行调度器SerialExecutor来执行,即由SerialExecutor来把FutureTask对象交给线程池来执行。
在发布结果时,首先由实现了Callable接口的WorkerRunnable进行处理,然后分情况发布执行结果和阶段性结果,用绑定了主线程Looper的InternalHandler把Message发送到主线程,然后再分情况进行处理。

我的理解:
AsyncTask主要用到了线程池、Callable、FutureTask和Handler。在AsyncTask中定义了两个线程池,一个是SerialExecutor,用于把提交的任务加入队列,然后再一个一个取出来,主要是保证串行执行任务。另一个线程池是THREAD_POOL_EXECUTOR,从SerialExecutor出队的消息就会传入到THREAD_POOL_EXECUTOR中去执行。
在AsyncTask构造方法中实例化mWorker(实现了Callable接口)和mFuture(实现了FutureTask)对象。在AsyncTask.execute(params)时,会调用excuteOnExecutor(exec,params)方法,传入SerialExecutor作为默认线程池,在excuteOnExecutor中,首先会判断status状态是不是PENDING(初始状态),如果不是PENDING就抛出异常,所以AsyncTask实例只能执行一次。是PENDING的话,接下来会把status设置为RUNNING、执行onPreExecute、把params赋值给mworker.mParams(此时还在主线程),之后把mFuture传递给exec.execute方法去执行,此处默认exec是SerialExecutor,SerialExecutor的execute方法主要就是把mFuture入队,然后一个一个的出队,出队后的mFuture传递给THREAD_POOL_EXECUTOR.execute真正去执行,此时才开始调用mWorker里面的call方法,开始进入子线程,在call方法里执行doInBackground(所以该方法在子线程),并把doInBackground的返回值(也就是耗时任务的执行结果result)传递到postResult中。在postResult(result)里,新建Message,Message的what是MESSAGE_POST_RESULT,obj就是传进来的result封装成的AsyncTaskResult。然后通过getHandler获取到主线程的Handler(Looper.getMainLooper),称为InternalHandler,通过InternalHandler把消息发送到主线程的消息队列里,在InternalHandler的handleMessage里通过MESSAGE_POST_RESULT处理消息:先通过isCancel()方法判断任务是否取消,如果为true则调用onCancel()(此时在主线程),否则调用我们重写的onPostExecute()(此时在主线程),并把state赋值为FINISHED。
如果在doInBackground中通过publishProgress发布阶段性执行结果时,会先创建一个Message,what是MESSAGE_POST_PROGREE,obj是阶段性结果progress,然后通过InternalHandler把阶段性结果传入主线程的消息队列。最后在InternalHandler的handleMessage 中通过MESSAGE_POST_PROGRESS处理消息:直接调用我们重写的onProgressUpdate(此时在主线程)。
任务执行完成后会FutureTask内部会自动调用mFuture的done()方法;在任务执行过程中,我们也可以随时调用FutureTask的cancel方法取消执行任务,任务取消后也会执行FutureTask的done()方法。在done()方法中通过get()获取子线程的执行结果,如果任务还没完成get方法会阻塞等待。然后把结果传入postResultIfNotInvoked方法,在postResult IfNotInvoked方法中,首先判断mTaskInvoke(用来标志任务是否开始执行)是否为true,如果不为true说明AsyncTask还没开始执行就被取消了或者因为其他原因结束了,此时获得的结果是null,通过postResult把该结果通过Handler传到主线程去处理(result不为null,即正常执行结束的结果已经在call方法中传递给了postResult,在done方法中不用考虑)。
简单一点:在AsyncTask的构造方法里声明了Callable和FutureTask,Callable的call方法里进行耗时操作,FutureTask的done方法里可以得到操作结果。AsyncTask.execute方法时,先执行onPreExecute,futureTask入队SerialExecutor线程池,然后出队给THREAD_POOL_EXECUTOR.execute,在futureTask的run方法里调用Callable的call方法,call方法里调用doInBackground,执行结果通过主线程的Handler在onCancel或onPostExecute中执行,调用publishProgress时,阶段性结果通过主线程的Handler在onProgressUpdate中执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值