前言
上一篇文章我们一起分析了Android消息机制的实现原理,通过分析我们知道,Android系统规定不能在主线程(UI线程)中执行耗时操作,这就需要我们在子线程中处理耗时操作,然后在执行完耗时操作后我们可以通过Handler发送一个消息给主线程通知其进行刷新UI等操作。如果对这一块知识点还不清楚,请参考前面的文章Android Handler-带你从源码理解消息机制。而本篇文章要说到的是AsyncTask这个类,这个类就是专门用来处理异步任务的,它完成了对Thread和Handler的封装,我们只需在它的doInBackground方法中执行我们的耗时操作,在执行完耗时操作后,它的内部会完成调用Handler向主线程发送完成消息的逻辑,而不需要我们自行去处理。接下来就让我们一起走进它的源码,来分析一下它内部的实现原理(文章参考sdk29源码进行分析)。
AsyncTask
1.基本用法:
在AsyncTask的源码注释中,列举了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");
}
}
AsyncTask是一个抽象的范型类,内部存在一个抽象方法doInBackground,我们需要创建一个类继承它,并实现它的doInBackground方法,在这个方法的内部我们来实现具体的耗时操作的逻辑。它的三个范型参数分别为Params, Progress, Result,其中Params为执行任务时发送给任务的参数类型,doInBackground方法中的参数类型就是和这个Params类型一致的,上例中指定成了URL类型;Progress为任务执行的进度类型,通常指定为Integer类型;最后一个Result为任务返回结果的类型。当我们要通过上面的DownloadFilesTask执行下载文件的操作时,我们只需创建DownloadFilesTask实例并调用它的execute方法即可,在execute方法中我们需要传入任务执行所需的参数,即Params范型所对应的类型参数。
2.执行步骤:
一个AsyncTask任务从开始执行到结束,一共会经历四个步骤,它们分别是:
2.1.onPreExecute:
在任务开始执行之前在主线程中被调用,通常用来进行一些准备工作,比如显示一个进度条。
2.2.doInBackground:
在执行完onPreExecute方法之后,该方法就会被调用。此方法用于执行耗时操作,异步任务的参数传递到此方法中,此方法会返回一个计算结果并将其传递到最后一步onPostExecute方法中。在操作执行的过程中,还可以调用publishProgress方法将操作执行的进度传递到onProgressUpdate方法中。
2.3.onProgressUpdate:
此方法用于在耗时操作执行的过程中,在用户界面以任意的形式展示当前操作执行的进度,该方法是在主线程中被调用的。
2.4.onPostExecute:
在耗时操作执行完毕时,该方法会在主线程中被调用,并且耗时操作返回的结果将被传入到此方法的参数中。
在前面的AsyncTask使用示例中,我们已经看到了其中的三个步骤方法,通常情况下,除了doInBackground方法是必须被重写的,其他三个方法我们可以根据自身的需求来选择性的去使用。
3.注意事项:
在使用AsyncTask类的时候,这里有一些注意事项还是要知道的:
- AsyncTask类必须在主线程中完成加载。
- AsyncTask实例必须在主线程中创建。
- AsyncTask的execute方法必须在主线程中调用。
- 不要手动调用它的onPreExecute、onPostExecute、doInBackground、onProgressUpdate方法。
- 一个AsyncTask任务只能被执行一次,否则会在第二次调用execute方法时抛出异常。
4.走进源码:
4.1构造方法:
AsyncTask的构造方法一共有三个,我们先来看下这三个方法的源代码:
public AsyncTask() { this((Looper) null); }
public AsyncTask(@Nullable Handler handler) { this(handler != null ? handler.getLooper() : null); }
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler() : new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return 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);
}
}
};
}
通常情况下我们调用它的无参构造来创建一个AsyncTask,而在这个无参构造的内部调用的是它的带有一个Looper 类型参数的构造,在这个构造方法中会初始化三个变量,分别为mHandler、mWorker、mFuture,而这三个实例对应的类型分别为InternalHandler、WorkerRunnable、FutureTask,这三个变量可以说是AsyncTask完成异步任务过程中的重要角色,把这三个变量的意义弄懂基本上后面的任务执行流程就能很轻松的理解了。
4.2.InternalHandler:
首先说一下这个InternalHandler,这个类是AsyncTask的一个静态内部类,继承自Handler,只不过它的内部重写了handleMessage方法,源码如下:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
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;
}
}
}
在它的handleMessage方法中,只会处理两种类型的消息,一种是MESSAGE_POST_RESULT类型的消息,也就是当任务执行完成时发送出的消息;而另一种是MESSAGE_POST_PROGRESS类型的消息,也就是在任务执行过程中更新进度的消息。在这个方法中可以看到一个AsyncTaskResult类,这个类也是AsyncTask的一个静态内部类,它的源码如下:
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
这个类可以看成是一个数据承载类,在AsyncTask的工作机制中它会被赋值成InternalHandler发送的消息中的obj变量,它会承载两种类型的数据,一种就是任务执行过程中的进度数据,一种就是任务执行完成的结果数据。在AsyncTaskResult类的内部拥有两个成员变量mTask和mData,其中mTask为AsyncTask类型,在所有创建AsyncTaskResult实例的地方都会将其指向当前的AsyncTask;另一个mData是一个数组,当AsyncTaskResult承载的是进度数据时,mData的类型会与AsyncTask类的Progress泛型一致;而当AsyncTaskResult承载的是结果数据时,mData的类型会与AsyncTask类的Result泛型一致。现在回到InternalHandler的handleMessage方法,当接收的消息为MESSAGE_POST_RESULT类型时,会调用到AsyncTask的finish方法,源码如下:
private void finish(Result result) {
if (isCancelled()) { // isCancelled方法的内部会判断当前的任务是否已经取消掉
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在finish方法的内部会进行判断,如果当前的任务已经被cancel掉了,那么就调用onCancelled方法(内部再调用onCancelled的无参重载方法,是一个空方法,可以根据需要来实现具体的逻辑);如果当前任务没有被cancel掉,就会调用前面我们说过的onPostExecute方法,这个方法本身也是一个空方法,我们可以重写这个方法并在方法内部对任务执行的结果进行相应的处理,最后在方法的结尾处会将mStatus变量的值置为Status.FINISHED,此时代表任务执行完成;再次回到InternalHandler的handleMessage方法中,当接收的消息为MESSAGE_POST_PROGRESS类型时,会调用AsyncTask的onProgressUpdate方法,这个方法前面也是说到过,也是一个空方法,我们可以重写这个方法在里面进行一些更新任务执行进度的提示操作。
4.3.WorkerRunnable:
这个WorkerRunnable是AsyncTask的一个静态内部类,它实现了Callable接口,并且它的范型和AsyncTask的两个范型相对应,源代码如下:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
在它的内部只有一个mParams数组变量,类型与Params范型一致,现在再来看一下在构造方法中mWorker变量的初始化方式:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
/**
* 这个mTaskInvoked是一个AtomicBoolean类型的变量;
* 一旦当前AsyncTask任务中的mWorker的call被调用,这个mTaskInvoked变量中的value值就会被置为1;
* 在AtomicBoolean类的内部存在一个value变量,值为1时代表true,值为0时代表false;
*/
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
因为WorkerRunnable是一个抽象类,所以只能通过匿名类的方式创建它的实例,其内部实现的call方法是Callable接口中的。在这个call方法的内部我们看到了一个非常关键的方法,即前面说到的用来执行耗时任务的doInBackground方法,通过doInBackground方法执行具体的耗时任务并返回一个任务结果,在call方法的最后会调用postResult方法将返回的任务结果发送给mHandler处理,方法的源码如下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在方法的内部就是通过mHandler发送处一条MESSAGE_POST_RESULT类型的消息,消息的obj变量为一个范型被指定Result类型的AsyncTaskResult对象,最终这个消息会被mHandler处理,至于消息处理的逻辑前面刚刚讲过这里不再赘述。通过分析mWorker的call方法,我们可以知道在AsyncTask中,mWorker扮演着任务的执行者角色,一个AsyncTask任务是在mWorker的call方法中被真正执行的。
4.4.FutureTask:
这个类并不是AsyncTask的内部类,也并不是单单只会出现在AsyncTask的使用场景中,它可以理解成一个将要被执行的任务,在执行之前存在被取消掉的可能,这个类间接的实现了Runnable接口。在前面的AsyncTask的构造方法中,它的实例化方式如下:
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);
}
}
};
这里调用它的一个参数的构造方法,将刚刚的mWorker变量传入FutureTask中,并且重写了它的done方法。在后面我们要说到的AsyncTask的execute方法中,这个mFuture变量最终会被交给一个线程池去处理,当线程池调用execute方法执行这个FutureTask时,这个FutureTask的run方法将会被调用(FutureTask实现了Runnable接口,这里需要对线程以及线程池的内部原理有一定的了解),而在FutureTask的run方法中,在构造方法中传入的mWorker变量的call方法又将会得到调用(WorkerRunnable实现了Callable接口),从而使得AsyncTask的任务得到真正的执行。关于FutureTask的run方法的源码如下:
public void run() {
........
try {
Callable<V> c = callable; // 这个callable就是构造方法中传入的Callable对象,即mWorker
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); // 在这里Callable对象的call方法将会被调用
ran = true;
} catch (Throwable ex) {
........
}
if (ran)
set(result);
}
} finally {
........
}
}
而在实例化mFuture对象时重写的这个done方法,在FutureTask类中的finishCompletion方法中会被调用,而finishCompletion方法存在三处被调用的地方,其中一处就在刚刚说到的run方法的结尾处调用的set方法内部,关于这个done方法内部调用的postResultIfNotInvoked方法,源码如下:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
在前面说到的mWorker的call方法中,mTaskInvoked变量会调用set方法传入true,此时如果我们调用mTaskInvoked的get方法获取到的返回值就为true。在AsyncTask中这个mTaskInvoked变量只有这一处调用set方法的地方,因此可以得出一个结论,一旦mWorker的call被调用了,那么在postResultIfNotInvoked方法中就不会执行postResult方法。正常情况下,只要调用了AsyncTask的execute方法开始执行任务,mWorker的call方法就会被正常调用,postResult方法也会在call方法的结尾处调用,而在done方法的内部就不会再次执行postResult方法;而如果当前的任务还未被执行,此时调用了AsyncTask的cancel方法取消任务,这时mWorker的call方法还未被调用,也就是说这时mTaskInvoked通过get方法返回的值为默认值false,那么此时如果mFuture的done方法被调用,postResult方法就会在其内部被调用。而AsyncTask的cancel方法内部其实就是调用mFuture的cancel方法,最终就会导致mFuture的done方法被调用,因此当一个AsyncTask任务如果在执行之前被取消掉,那么它的postResult方法会在mFuture的done方法中被调用。
4.5.execute方法:
在了解了AsyncTask的构造方法的逻辑之后,我们来看一下它的开启任务执行的execute方法:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
方法的内部继续调用executeOnExecutor方法,executeOnExecutor方法接收两个参数,第一个参数是一个实现了Executor接口类型的参数,第二个参数为execute方法中传入的最终执行任务所需的参数。在execute方法中传给executeOnExecutor方法的第一个参数为sDefaultExecutor变量,sDefaultExecutor是AsyncTask类的一个静态变量,这个变量的类型为SerialExecutor类型,而SerialExecutor也是AsyncTask的一个静态内部类。关于这个SerialExecutor类后面再做详细分析,我们先来看一下executeOnExecutor方法的源码:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
1 if (mStatus != Status.PENDING) {
2 switch (mStatus) {
3 case RUNNING:
4 throw new IllegalStateException("Cannot execute task:"
5 + " the task is already running.");
6 case FINISHED:
7 throw new IllegalStateException("Cannot execute task:"
8 + " the task has already been executed "
9 + "(a task can be executed only once)");
10 }
11 }
12 mStatus = Status.RUNNING;
13 onPreExecute();
14 mWorker.mParams = params;
15 exec.execute(mFuture);
16 return this;
}
首先,对mStatus 变量的值进行判断,这个mStatus 是一个枚举类型的变量,它的值有三种可能:
1.Status.PENDING: 任务尚未执行。
2.Status.RUNNING: 任务正在执行。
3.Status.FINISHED: 任务已经执行完成。
默认情况下mStatus的值为Status.PENDING,在AsyncTask中mStatus存在两处赋值的地方,一处是前面说到的finish方法中会将其置为Status.FINISHED,另一处就是当前的executeOnExecutor方法的第12行会将其置为Status.RUNNING。
现在回到executeOnExecutor方法,当mStatus的值不等于Status.PENDING时方法就会抛出异常,这也就验证了AsyncTask的execute方法确实只能调用一次,当mStatus的值为Status.PENDING时,说明此时任务还未被执行,那么就向下执行调用前面说到的onPreExecute方法(注意此时异步任务还未真正开始执行),这个方法本身是一个空方法,如果我们需要在任务正式开始执行前做一些什么准备操作的话可以重写该方法来编写适当的逻辑;接着将传给任务的参数赋值给mWorker的mParams变量,这个mParams最终会在mWorker的call方法中传给AsyncTask的doInBackground方法;最后在第15行会执行Executor的execute方法来执行当前的mFuture任务了,接下来就要揭晓AsyncTask中的两个最关键的角色了。
4.6.线程池SerialExecutor:
前面我们已经提到过,在AsyncTask的execute方法中传给executeOnExecutor方法的第一个参数为sDefaultExecutor变量,而sDefaultExecutor是一个SerialExecutor类型的静态变量,那么我们来看一下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(); // 内部会调用mWorker的call方法去执行doInBackground方法
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
在SerialExecutor的内部,拥有两个成员变量mTasks和mActive,其中mTasks为范型为Runnable的队列,用于存储Runnable对象,在AsyncTask类中就是FutureTask对象。在execute方法中,首先调用ArrayDeque的offer方法将传入的FutureTask对象添加到mTasks的结尾,然后判断mActive变量是否为空,如果为空就调用scheduleNext方法,在scheduleNext方法中首先会将mActive指向mTasks中的第一个元素(ArrayDeque的poll方法返回队列中的第一个元素),如果mActive不为空,就会去调用THREAD_POOL_EXECUTOR线程池的execute方法执行当前的mActive,而在线程池的execute方法中,mActive的run方法最终就会被调用(这一点前面也提到过,需要对线程池和线程的执行原理有一定的了解)。现在再来看下刚刚在调用mTasks的offer方法添加FutureTask对象时重写了FutureTask的run方法,在run方法中先是执行FutureTask自身的run方法,然后会接着调用scheduleNext方法,如果mTasks中还有FutureTask对象,就继续执行下一个FutureTask,由此可知AsyncTask中的任务其实是串行执行的。
4.7.THREAD_POOL_EXECUTOR:
到这里其实AsyncTask任务的执行流程就差不多分析完了,只不过还有一个重要角色没介绍,那就是执行FutureTask的线程池THREAD_POOL_EXECUTOR,它是AsyncTask的一个静态变量,就是一个正常的ThreadPoolExecutor类型的线程池,它的核心线程数为1,最大线程数为20,这也就意味着当使用默认的THREAD_POOL_EXECUTOR线程池时AsyncTask允许同时执行的任务最多有20个。如果觉着默认的线程池不满足你的需要,我们也可以通过调用AsyncTask的setDefaultExecutor方法为当前的AsyncTask设置自定义的线程池来执行任务。
总结
AsyncTask的主要知识点以及任务的执行机制基本上就分析完了,在它的内部有几个重要的角色如下:
1.AsyncTask的内部存在两个线程池,一个是SerialExecutor类型的sDefaultExecutor,它用来存储将要被执行的FutureTask任务,使任务按顺序串行执行;另一个THREAD_POOL_EXECUTOR为ThreadPoolExecutor类型,它用于执行FutureTask任务。
2.InternalHandler类型的mHandler对象负责处理任务执行过程中的消息传递。
3.FutureTask类型的mFuture对象,代表一个将要被THREAD_POOL_EXECUTOR执行的任务。
4.WorkerRunnable类型的mWorker对象,是任务的真正执行者,它被mFuture对象持有。
当调用构造方法创建一个AsyncTask实例时,会在构造方法中创建三个变量,分别为传递消息的mHandler、任务的真正执行者mWorker以及将要被执行的任务mFuture,其中mWorker会被传入到mFuture的内部,当mFuture的run方法被调用时,mWorker的call方法就会被调用,最终doInBackground方法就会被执行,耗时任务就得以执行;当创建完AsyncTask实例后,调用它的execute方法将mFuture添加到sDefaultExecutor中,最终再交给THREAD_POOL_EXECUTOR的execute方法去处理,这时mFuture的run方法就会被调用。至此,整个AsyncTask的工作机制就已经分析完了!最后,如果文章对您有帮助,还希望点赞支持下!