前言
AsyncTask在Android编程中是一种常用的异步编程方式,那么AsyncTask到底是什么呢?下面我们从
基本使用到源码分析对AsyncTask作一个全面的了解。
一般使用
通常我们对AsyncTask的使用是
//1.实现抽象类AsyncTask
Class MyAsyncTask extends AsyncTask{
@Override
protected Object doInBackground(Object[] params) {
return null;
}
}
//2.创建实例对象
MyAsyncTask myAsyncTask = New MyAsyncTask();
//3.调用execute方法执行任务
myAsyncTask.execute();
一般使用比较简单,通过实现AsyncTask抽象类,然后创建一个这样的子类实例,然后调用execute方法就可以在doInBackground进行异步处理了。
源码分析
我们看看execute方法的具体实现(这里针对Android-23源码分析的,不同版本的源码实现的方式可能不同,这里不一一分类了)
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
一句代码,我们看看executeOnExecutor方法的具体实现
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;
}
首先检测mState的状态,如果同一个AsyncTask实例在没有pending的情况下,再进行execute,那么会报出相应的异常;然后去设置mState的状态,调用onPreExecute,去执行exec.execute(mFuture),最终的执行在这里。那么我们看看刚才传进来的sDefaultExecutor参数。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new 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();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
实质上是调用SerialExecutor 类的execute方法,首先向mTasks 提交offer队列的末尾,然后从mTasks 的头部poll出要执行的runnable.
从名字就可以看出,默认情况下,多个task是加到mTask后执行的。也就是串行执行的。如果要并行执行的画,通过executeOnExecutor(Async.THREAD_POOL_EXECUTOR,params);
关于doInBackground、onPreExecute、onPostExecute、onProgressUpdate等几个抽象方法的调线程和时期关系,不想做太多分析,都是模版设计模式思想,在不同地方调用罢了。比较简单。这里主要将线程池,这里才是AsyncTask的精华所在,以及以后在Android系统使用单独的线程池时候有个很好的参照。
线程池参数解释
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
corePoolSize: 核心线程数目,即使线程池没有任务,核心线程也不会终止(除非设置了allowCoreThreadTimeOut参数)可以理解为“常驻线程”
maximumPoolSize: 线程池中允许的最大线程数目;一般来说,线程越多,线程调度开销越大;因此一般都有这个限制。
keepAliveTime: 当线程池中的线程数目比核心线程多的时候,如果超过这个keepAliveTime的时间,多余的线程会被回收;这些与核心线程相对的线程通常被称为缓存线程
unit: keepAliveTime的时间单位
workQueue: 任务执行前保存任务的队列;这个队列仅保存由execute提交的Runnable任务
threadFactory: 用来构造线程池的工厂;一般都是使用默认的;
handler: 当线程池由于线程数目和队列限制而导致后续任务阻塞的时候,线程池的处理方式。
线程池核心调度思想
那么,当一个新的任务到达的时候,线程池中的线程是如何调度的呢?
- 如果线程池中线程的数目少于corePoolSize,就算线程池中有其他的没事做的核心线程,线程池还是会重新创建一个核心线程;直到核心线程数目到达corePoolSize(常驻线程就位)
- 如果线程池中线程的数目大于或者等于corePoolSize,但是工作队列workQueue没有满,那么新的任务会放在队列workQueue中,按照FIFO的原则依次等待执行;(当有核心线程处理完任务空闲出来后,会检查这个工作队列然后取出任务默默执行去)
- 如果线程池中线程数目大于等于corePoolSize,并且工作队列workQueue满了,但是总线程数目小于maximumPoolSize,那么直接创建一个线程处理被添加的任务。
- 如果工作队列满了,并且线程池中线程的数目到达了最大数目maximumPoolSize,那么就会用最后一个构造参数handler处理;默认的处理方式是直接丢掉任务,然后抛出一个异常。
具体的实现场景描述:当有新的任务要处理时,先看线程池中的线程数量是否大于 corePoolSize,再看缓冲队列 workQueue 是否满,最后看线程池中的线程数量是否大于 maximumPoolSize。另外,当线程池中的线程数量大于 corePoolSize 时,如果里面有线程的空闲时间超过了 keepAliveTime,就将其移除线程池,这样,可以动态地调整线程池中线程的数量。