AsyncTask 是 Android 中一个异步处理的框架,它内部集成了线程池和 Handler 机制,实现了异步任务加载和主线程更新 UI 的功能,不过在 Android 中不同的版本的 AsyncTask 却有点不一样,下面就看看不同版本间内部的线程池有什么区别。
1、不同版本 AsyncTask 的获取
** 源码地址 **
AsyncTask 2.3.7
AsyncTask 4.0.1
AsyncTask 4.0.4
2、线程池的几个属性的说明
在分析版本区别之前我们先来了解以下关于线程池 ThreadPoolExecutor 的几个属性的概念:
corePoolSize 线程池中核心线程的数量,当有新的任务到来时,若当前线程池的线程数量小于 corePoolSize ,那么就直接创建一个新的线程取执行。
maximumPoolSize 线程池中能够运行的最大线程的数量,若一个新的任务到来,此时线程池中线程的数量超过了 corePoolSize ,但没有超过 maximumPoolSize时,这时会判断阻塞队列 workQueue 是否还有多余的空间来存储该任务,如果有多余空间,则会往队列中添加,否则就会创建非核心线程去执行这个任务(前提是当前创建的非核心线程与核心线程的数量之和不会超过 maximumPoolSize)。
keepAliveTime 执行完任务的线程最长的等待时间,若这个时间段内,没有其他任务交给此线程执行,当前线程就会被终止。
unit keepAliveTime 的单位,有毫秒,秒,分等;
workQueue 阻塞队列,当有新的任务到来时,若当前线程池的线程数量大于 corePoolSize 并小于 maximumPoolSize 的话,那么就将该任务添加到 workQueue 中。
threadFactory 创建线程的工厂。
handler 当有一个新的任务到来时,发现阻塞队列已经满了,并且当前池中的线程数量超过了 maximumPoolSize 时,那么就会采取一定的策略 RejectedExecutionHandler 去拒绝这个任务。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
3、Android3.0以下版本
在 Android3.0 以下的版本中,AsyncTask 默认就支持并发执行任务的,它对内部的线程池 sExecutor 配置如下:
3.1、线程池
- 核心线程数为 5 个;
- 最大线程数为 128 个;
- 阻塞队列为 LinkedBlockingQueue 容量为 128 ;
- sExecutor 就是默认的线程池
//核心线程数
private static final int CORE_POOL_SIZE = 5;
//最大线程数
private static final int MAXIMUM_POOL_SIZE = 128;
//等待时间
private static final int KEEP_ALIVE = 1;
//阻塞队列
private static final BlockingQueue sWorkQueue =
new LinkedBlockingQueue(10);
//线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread More ...newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//线程池
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
3.2、execute方法
在 execute 方法可以看到,内部会将任务直接交给 sExecutor 去执行的,因此在 Android 3.0 以下的版本是默认就支持并发执行的。
public final AsyncTask More ...execute(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;
//将任务直接交给 sExecutor 线程池去执行。
sExecutor.execute(mFuture);
return this;
}
4、Android3.0 ~4.4版本
4.1、线程池
- 核心线程数为 5 个;
- 最大线程数为 128 个;
- 阻塞队列为 LinkedBlockingQueue 容量为 10 ;(变化之处)
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(10);
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//默认的线程池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//自定义了一个线程池 THREAD_POOL_EXECUTOR(变化之处)
public static final Executor THREAD_POOL_EXECUTOR= new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
4.2、默认的线程池 SerialExecutor
SerialExecutor是什么?
顾名思义,它是一个串行执行的线程池,所有的任务都会一个一个按顺序执行,可以任务是同步执行的效果,它是一个高版本的 AsyncTask 的默认线程池,因此默认是不支持并发执行的。
SerialExecutor的工作原理是怎样的?
内部维护了一个 ArrayDeque 的队列,它是一个无限制大小的队列。
它内部会将即将要执行的任务添加到 mTasks 中,然后去调用 scheduleNext 方法,而这个方法实际是调用了 THREAD_POOL_EXECUTOR.execute 方法
SerialExecutor在执行完一个任务之后,怎么去执行下一个任务的?
当任务执行完毕之后在 finally 中再次调用 scheduleNext 方法去执行下一个任务,这样就相当于一个顺序执行的线程池,此时的 AsyncTask 默认情况下是不支持并发执行的。
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//存储任务到队列中
mTasks.offer(new Runnable() {
public void More ...run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//取出一个任务
if ((mActive = mTasks.poll()) != null) {
//最终将任务交给 THREAD_POOL_EXECUTOR 线程池去执行。
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
4.3、3.0以上版本的 execute 方法
它内部会去将 sDefaultExecutor(就是上面所说的默认的线程池 SerialExecutor ) 传入给 executeOnExecutor 方法,该方法会将该任务直接交给 sDefaultExecutor 线程池去执行的,这里就说明了默认情况下是不支持并发的,而是串行执行的。
executeOnExecutor 方法会在下面分析。
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
4.4、修改默认的线程池
为什么Google要提供修改线程池的API?
因为在 3.0 之后 AsyncTask 默认是串行执行任务的,为了提供并发执行的功能,因此提供了 executeOnExecutor 方法让调用者可以自己去配置对一个的线程池。
高版本实现并发执行
使用 executeOnExecutor 方法代替 execute 方法,参数1表示调用者提供的线程池,参数2跟 execute 方法的参数是一样的,表示异步执行需要的参数。
public final AsyncTask
从下面的代码可以看到,它是直接将 mFuture 交给传入的线程池 exec 去执行的。
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
...
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
//将任务交给调用者提供的线程池去执行。
exec.execute(mFuture);
return this;
}
4.5、示例代码
在下面的示例代码中可以看到,所有的创建的异步任务 MyAsyncTask 都共享一个线程池 AsyncTask.THREAD_POOL_EXECUTOR ,这样就可以实现并发操作了。
for (int i = 0; i < 10; i++) {
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "task:" + i);
}
public class MyAsyncTask extends AsyncTask {
@Override
protected String doInBackground(String[] params) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return params[0];
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.e("zeal", s + "#onPostExecute");
}
}
5、Android4.4 及之后的版本
可以在 Android4.4 之后线程池的配置上又发生了一些改变,但是核心操作思想跟上面的是一样的。
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//虚拟机的处理数
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 sPoolWorkQueue =new LinkedBlockingQueue(128);
好了,以上就是我所知道的 AsyncTask 在不同版本间线程池的差别,可能还有其他方面的区别,有待以后的发掘。