一、AsyncTask 简介
AsyncTask
public abstract class AsyncTask
extends Object
java.lang.Object
↳ android.os.AsyncTask< Params, Progress, Result >
AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类,它可以很方便的使用UI线程,执行后台任务,并可以把执行的程序和运行的结果给Ui线程处理,而无需实现Thread和handler。
二、AsyncTask 使用
1、AsyncTask的泛型类型
异步任务使用的三种类型如下:
Params : 执行时发送给任务的参数类型。
Progress : 后台执行任务进度的进度类型。
Result : 异步任务最终返回的结果类型。
并非所有类型都始终由异步任务使用。如果不指定,只需使用以下类型Void:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
2、五个方法
执行异步任务时,任务将执行4个方法:
1、onPreExecute()
,刚开始执行的时候调用,可以用于进行一些界面上的初始化操作,比如说显示一个进度条对话框
2、doInBackground(Params...)
,在onPreExecute()
完成执行后立即在后台线程上调用,执行在异步线程, 耗时的操作在这个方法中执行。返回结果被传递到 onPostExecute(), 在执行任务时调用publishProgress()
把执行进度传递给onProgressUpdate()
。
3、onProgressUpdate(Progress...)
执行在UI线程, 更新进度信息, 调用publishProgress()
时被回调。
4、onPostExecute(Result)
,执行在UI线程, 一般在执行完后台任务后更新UI的操作, 显示结果等
除了上面四个方法,AsyncTask还提供了onCancelled()
方法,它同样在主线程中执行,当异步任务取消时,onCancelled()
会被调用,这个时候onPostExecute()
则不会被调用,但是要注意的是,AsyncTask中的cancel()
方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()
判断终止任务。就好比想要终止一个线程,调用interrupt()
方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
3、AsyncTask 使用的实例
我在前面的博客里写了一个AsyncTask 使用OKHttp 实现断点下载大文件实例,效果图如下:
想了解怎么使用的可以看Android 启动服务配合AsyncTask 使用OKHttp 实现断点下载大文件实例这篇文章。
三、 AsyncTask 源码分析
我打开的是Android 8.1 版本的AsyncTask 源码;
1、构造函数:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
* 创建一个新的异步任务。, 必须在UI线程上调用此构造函数。
*/
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
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 {
...
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
}
};
}
在构造函数里面,能看到创建了三个变量,分别是mHandler
、mWorker
以及mFuture
。
(1)mHanlder
因为第二个以及第三个构造函数都被隐藏的缘故,所以 mHandler
只能是 getMainHandler()
,我们看下这个方法的实现。
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
AsyncTask
里面使用的是InternalHanler
,里面绑定了主线程的Looper
和消息队列。如果handler
收到的是MESSAGE_POST_RESULT
消息,就会执行finish()
方法,最后调用onPostExecute()
方法。如果handler
收到的是MESSAGE_POST_PROGRESS
消息,就会执行onProfressUpdate()
方法。
(2)mWorker
mWorker 是 WorkerRunnable 类型的内部类对象:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
WorkerRunnable
实现了Callable
接口,接口中只有一个 call()
方法。因为不论是继承Thread
类还是实现Runnable
方法,执行完任务以后都无法直接返回结果。而Callable
接口弥补了这个缺陷,当call()
方法执行完毕以后会返回一个泛型对象,而mWorker
重写了 call
方法。
Callable
接口实际上是属于Executor
框架中的功能类,Callable
接口与Runnable
接口的功能类似,但提供了比Runnable
更强大的功能,主要表现为以下三点:
(1)Callable
可以在任务接受后提供一个返回值,Runnable
无法提供这个功能。
(2)Callable中
的call()
方法可以抛出异常,而Runnable
的run()
方法不能抛出异常。
(3)运行Callable
可以拿到一个Future
对象,Future
对象表示异步计算的结果,他提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下就可以使用Future
来监视目标线程调用call()
方法的情况,但调用Future
的get()
方法以获取结果时,当前线程就会阻塞,直到call()
方法的返回结果。
我们继续看下call()
方法里面执行了什么:
public Result call() throws Exception {
//先设置mTaskInvoked为true,表示线程已经开始
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;
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
首先要明确的一点,call()
一定是在线程池的子线程中执行的,并没有执行在主线程中。当call()
执行的时候,先设置mTaskInvoked
为true
,表示线程已经开始。接下来设置当前的线程级别为标准优先级后台线程级别,使您的线程略低于正常优先级,因此它将不太可能影响用户界面的响应性。然后在子线程中执行doInBackground()
方法并将处理结果返回,最后将执行完的结果交给postResult()
。继续看postResult()
方法,getHandler
获取的其实就是刚刚定义的mHandler
,这时mHandler
就会收到的是MESSAGE_POST_RESULT
消息,就会执行finish()
方法,最后调用onPostExecute()
方法。
(3)mFuture
public class FutureTask<V> implements RunnableFuture<V> {
...
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
* 将此Future设置为其计算结果,除非它已被取消。
*/
void run();
}
mFuture 实现了RunnableFuture
接口,而Runnablefuture
最终继承Runnable 和 Future
接口,这里我们说一下Future
接口:
在Future
接口中声明了5个方法,下面依次解释每个方法的作用:
1、cancel
方法用来取消任务,如果取消任务成功则返回true
,如果取消任务失败则返回false
。参数mayInterruptIfRunning
表示是否允许取消正在执行却没有执行完毕的任务,如果设置true
,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning
为true
还是false
,此方法肯定返回false
,即如果取消已经完成的任务会返回false
;如果任务正在执行,若mayInterruptIfRunning
设置为true
,则返回true
,若mayInterruptIfRunning
设置为false
,则返回false
;如果任务还没有执行,则无论mayInterruptIfRunning
为true
还是false
,肯定返回true
。
2、isCancelled
方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
3、isDone
方法表示任务是否已经完成,若任务完成,则返回true;
4、get()
方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
5、get(long timeout, TimeUnit unit)
用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说Future
提供了三种功能:
(1)判断任务是否完成;
(2)能够中断任务;
(3)能够获取任务执行结果。
因为Future
只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask
。所以FutureTask
既能当做一个Runnable
直接被Thread
执行,也能作为Future
用来得到Callable
的计算结果。
我们来看下mFuture
的done
方法实现了哪些内容:
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);
}
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
当任务的状态的变化时就会执行done 此方法,然后可以看到执行了postResultIfNotInvoked()
方法。当wasTaskInvoked
为false
的时候执行postResult(result)
方法,但是在执行mWorker
的call()
方法的时候,已经将wasTaskInvoked
设为了true
。所以当任务执行完后或者取消后才会执行postResult(result)
这个方法。
2、execute 方法
在实例化了AsyncTask对象之后,我们就可以调用AsyncTask的execute方法执行任务,execute代码如下所示:
@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:
//如果当前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();
// sDefaultExecutor 执行 mFuture
mWorker.mParams = params;
exec.execute(mFuture);
//最后将AsyncTask自身返回
return this;
}
在execute
方法上,注意到有@MainThread
,所以这个方法是在主线程中执行的。在方法里执行了executeOnExecutor()
方法,并把sDefaultExecutor
也传了进去,我们先看executeOnExecutor()
方法。一开始先判断了AsyncTask
的状态,当状态不是PENDING
即任务尚未开始执行时,抛出两个异常并不再执行,这就意味着Asynctask
只能执行一次。当AsyncTask
的状态为尚未开始执行的时候,将状态改为运行状态并执行onPreExecute()
方法,用 mWorker.mParams
保存传入的参数,然后调用sDefaultExecutor
执行 mFuture
。
sDefaultExecutor
是SerialExecutor
的一个实例:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
// mTasks是一个维护Runnable的双端队列实例,ArrayDeque没有容量限制,其容量可自增长
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
// mActive 正在执行的任务
Runnable mActive;
public synchronized void execute(final Runnable r) {
// 向队尾插入一个Runnable
mTasks.offer(new Runnable() {
public void run() {
try {
// 开始执行任务
r.run();
} finally {
// 调用scheduleNext() 执行下一个Runnable
scheduleNext();
}
}
});
// 如果mActive 为空 执行下一个Runnable
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
// mTasks.poll() 取出队列头部的元素,并从队列中移除
// 将取出队列的Runnable 赋值给mActive,如果不为空则交给THREAD_POOL_EXECUTOR去执行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExecutor
是一个串行的线程池,当sDefaultExecutor
执行execute()
方法的时候,开始执行SerialExecutor
类的execute()
方法,会向mTasks
的队尾添加一个新建的Runnable
,其内部会调用 r.run()
;无论任务r
正常执行完成还是抛出异常,都会在finally
中执行scheduleNext
方法,用于执行mTasks
中的下一个任务。因此,我们可以看出SerialExecutor
是一个接一个的处理任务,是串型执行任务,而不是并行执行任务。当mActive
为空的时候, 也会开始执行下一个Runnable
。这里的任务都是交给THREAD_POOL_EXECUTOR
(线程池)去处理的,我们看下THREAD_POOL_EXECUTOR
:
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
// 创建一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
//允许核心线程超时
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
THREAD_POOL_EXECUTOR
指的就是threadPoolExecutor
,他的核心线程和线程池允许创建的最大线程数都是由CPU的核数来计算出来的,它采用的阻塞队列是LinkedBlockingQueue
。
总结
关于源码的讲解到这里就结束了,我们来梳理下整个流程。当我们继承AsyncTask
实现自定义的异步任务时,会初始化三个值,分别是mHandler
、mWorker
以及mFuture
。然后当我们调用AsyncTask
的execute
方法执行任务的时候,在execute()
方法里先判断一下状态,然后将状态改为运行状态并执行onPreExecute()
方法,用 mWorker.mParams
保存传入的参数,然后通过SerialExecutor
线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,最后再调用THREAD_POOL_EXECUTOR
线程池去真正地执行任务mFuture
,进而会执行mWorker
的call()
方法,先设置mTaskInvoked
为true
,表示线程已经开始。接下来设置当前的线程级别为标准优先级后台线程级别,然后在子线程中执行doInBackground()
方法并将处理结果返回,最后将执行完的结果交给postResult()
。在postResult
方法中,获取到mHandler
,这时mHandler
就会收到的是刚刚发出的MESSAGE_POST_RESULT
消息,然后执行finish()
方法,最后调用onPostExecute()
方法,并将AsyncTask
的状态设置为完成状态。