简介
AsyncTask是Android提供的一种方便的线程转换操作的简易框架,可以让使用者在UI线程调用,准备,然后在子线程中执行耗时操作,并且在执行过程中不停地通知主线程,最后子线程任务完成之后还可以将结果回调到UI线程。
public abstract class AsyncTask<Params, Progress, Result> {
其中Params是执行子线程任务的时候传入的参数,Progress是在子线程任务进行过程中通知UI线程的时候传的参数类型,Result是子线程任务结束之后传递给UI线程的结果。
以下分析基于Android的API25的源码:
参数
可以跳过,主要是用于参考
//当前可用CPU核心数目
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心线程数至少两个,最多4个,CPU_COUNT-1是为了节省一个核心用于其它工作
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//线程池中最大线程数目2 * 可用CPU核心 + 1
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//线程最大空闲时间,超过30s会被回收
private static final int KEEP_ALIVE_SECONDS = 30;
//最大容纳128个任务,如果还有任务进入,会阻塞等待有任务执行完毕再进入队列中
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
//所有AsyncTask子线程任务实际执行的时候使用的线程池
//不过这个在sDefaultExecutor的调度下默认是串行的,只不过是在当前线程池中执行
public static final Executor THREAD_POOL_EXECUTOR;
//主线程Handler
private static InternalHandler sHandler;
//主线程Handler的回调码
//子线程任务完成,将Result回调到主线程
private static final int MESSAGE_POST_RESULT = 0x1;
//子线程任务进行中,尝试将Progress通知主线程的Handler
private static final int MESSAGE_POST_PROGRESS = 0x2;
//在子线程中进行任务的串行调度的一个线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//线程中的工作者,用于生产Result
private final WorkerRunnable<Params, Result> mWorker;
//线程池中的实际执行的计算任务,等待Result的计算完成
private final FutureTask<Result> mFuture;
//当前AsyncTask的执行状态
private volatile AsyncTask.Status mStatus = AsyncTask.Status.PENDING;
//当前AsyncTask是否取消,线程安全
private final AtomicBoolean mCancelled = new AtomicBoolean();
//当前子线程任务是否开始,线程安全
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
/**
* AsyncTask状态的枚举
*/
public enum Status {
//当前任务还没有开始
PENDING,
//当前任务执行中
RUNNING,
//当前任务完全执行结束,在主线程处理Result之后
FINISHED,
}
可以看到AsyncTask使用了状态模式,通过状态枚举来管理任务的进行流程。
初始参数
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;
}
实际上就是通过之前的参数建立了一个线程池,这个线程池最少两个最多4个核心线程,最多可以容纳2 * 可用CPU核心 + 1个线程,其中核心线程可以被回收。线程的最大空闲时间为30s。
后面会结合SERIAL_EXECUTOR作进一步的分析
运行流程
/**
* 在主线程调用,开始执行任务
* @param params 任务执行时候需要输入的参数
*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//默认通过SERIAL_EXECUTOR执行
return executeOnExecutor(sDefaultExecutor, params);
}
/**
* 在主线程中调用
* 在制定的线程池中开始执行任务
* @param exec 需要执行任务的线程池
* @param params 执行参数
*/
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//一个AsyncTask只能执行一次
if (mStatus != AsyncTask.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 = AsyncTask.Status.RUNNING;//标记Task状态为运行中
onPreExecute();//第一个回调方法
mWorker.mParams = params;//记录当前任务的参数
exec.execute(mFuture);//在线程池中执行任务
return this;
}
注意到一个AsyncTask只能执行一次,无法重复执行。
虽然AsyncTask的注解标记在主线程中执行,但是实际上执行在子线程也是可以的,不过这个时候onPreExecute()就是回调在子线程。简单说就是onPreExecute()执行在调用execute()的线程中。
AsyncTask可以通过executeOnExecutor执行在自定义的线程池中,不过这样就脱离AsyncTask的任务管理。这个根据自己的使用场景来定。
接着看mFuture任务:
/**
* 一个子线程中执行的Callable,具有计算结果Result
* 内部持有执行参数
*/
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//该方法是在线程池中的线程执行
mTaskInvoked.set(true);//标记线程任务开始
Result result = null;
try {
//设置线程优先级为标准子线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//将参数交给doInBackground开始任务
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);//出现异常的时候标记任务取消
throw tr;
} finally {
//会尝试将任务发送到主线程Handler中进行回到
postResult(result);
}
//保存计算结果,在FutureTask中可以通过get()获得
return result;
}
};
//mWorker是实际的计算任务
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
//一般来说到这里mWorker的call()都是执行完成的
//除非当任务被取消了,但是此时还没有开始call()
//这个时候也要发送结果出去
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);
}
}
};
}
/**
* 将子线程任务的结果发送给主线程handler
* @param result 子线程任务的结果
*/
private Result postResult(Result result) {
//向主线程Handler发送任务执行完毕消息
//结果封装在AsyncTaskResult中
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
/**
* 单例的主线程handler,这里可以理解为懒加载
* 否则一般来说可能会通过static final直接建立Handler
*/
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
/**
* 如果当前子线程任务还没有开始就已经被取消的时候尝试发送结果到主线程handler
*/
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
/**
* 取消当前任务
* @param mayInterruptIfRunning true的话会尝试中断运行中的线程
*/
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);//标记任务被取消
//尝试取消FutureTask的任务
return mFuture.cancel(mayInterruptIfRunning);
}
AsyncTask通过一个FutureTask在线程池中进行计算任务Callacble,并且等待计算结果。
如果任务被取消或者完成之后会通过主线程Handler发送消息进行后续的回调操作。
/**
* 主线程Handler,处理消息回调
*/
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());//主线程Looper,意味着handleMessage在主线程执行
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@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);//这里就是回调onProgressUpdate(Progress)方法
break;
}
}
}
/**
* 处理任务完成
* @param result
*/
private void finish(Result result) {
if (isCancelled()) {//当前任务已经被取消
onCancelled(result);//回调onCancelled
} else {
onPostExecute(result);//回调onPostExecute
}
mStatus = AsyncTask.Status.FINISHED;//标记当前任务完成
}
/**
* 最好在工作线程中调用,比方说doInBackground
* @param values 要发送的值
*/
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {//当前任务没有取消
//将当前任务进度发送到主线程Handler中,并且进行回调
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
可以看到onProgressupdate、onPostExecute和onCancelled都是在UI线程中被调用,分别代表的是任务进行中、任务正常完成、任务被取消。
最后看一下调度的线程模型
/**
* 默认的执行任务的线程池
*/
private static class SerialExecutor implements Executor {
//实际上这个线程池是static的,意味着所有AsyncTask都是共用这个任务队列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
//当前执行的任务
Runnable mActive;
/**
* 该方法实际上是调用在execute的调用线程中的
* 串行执行任务
*/
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();//执行FutureTask任务
} finally {
scheduleNext();//尝试执行下一个任务
}
}
});
//当前如果有任务执行,等待执行中的任务执行完成之后再尝试执行下一条任务
if (mActive == null) {
//当前没有任务在执行
scheduleNext();//尝试执行下一条任务
}
}
/**
* 如果当前任务队列中还有任务,在子线程中执行下一条任务
*/
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {//尝试获取当前队列中下一条任务
THREAD_POOL_EXECUTOR.execute(mActive);//在子线程执行任务
}
//没有任务直接结束,等待下一次调用
}
}
可以看到AsyncTask首先在execute的调用线程中开启任务,任务的执行条件必须满足之前的任务执行完成或者没有任务,即任务是串行执行的。
然后实际的任务是执行在THREAD_POOL_EXECUTOR线程池中的子线程,结合之前的参数可以稍微分析一下,当第一个任务开始,线程池会创建一个核心线程然后执行,这个时候如果有其它任务想要开始,则会进入任务队列中等待执行。
等上一次任务执行完成之后,将会执行下一个任务,此时如果超过上次执行任务完成30s之后,此时核心线程被回收,则会重新创建,否则在很快的时间内都会一直复用之前创建的核心线程,
所以说AsyncTask最好用于一些快速完成的任务。
总结
AsyncTask默认是串行执行任务的,允许执行在自定义的线程池中。
如果想要最大化利用线程池,最好保证AsyncTask的任务足够快。
每一个AsyncTask只能执行一次,不应许重复执行。
AsyncTask可以在子线程execute(),此时onPreExecute()会在子线程调用,官方还是推荐在UI线程中execute()。
onProgressUpdate一定执行在UI线程中,onCancelled和onPostExecute互斥且一定执行在UI线程中。