首先回顾一下AsyncTask的用法,他是一个抽象类,需要我们继承实现一些方法,常用的方法如下:
onPreExecute() //异步任务开始前调用
doInBackground(Params...) //异步任务执行的地方
onPostExecute(Result) //异步任务执行后调用
onProgressUpdate(Progress...) //异步任务执行中前台更新的回调
publishProgress(Progress... values) //调用此方法会回调onProgressUpdate
onCancelled(Object) //AsyncTask取消时的回调
isCancelled() //判断是否已取消
另外AsyncTask还有很重要的3个泛型
AsyncTask
根据名称可以很清楚的了解到,第一个表示后台任务接受的参数类型,第二个表示前台更新时接受的类型,最后一个表示后台执行完毕后返回的参数类型。
仅仅介绍很难有直观的感觉,下面先提供一个示例:
class MyTask extends AsyncTask{
@Override
protected void onPreExecute() {
super.onPreExecute();
tv.setText("开始执行");
bt.setEnabled(false);
}
@Override
protected Integer doInBackground(Integer... integers) {
int current = 0;
publishProgress(current,integers[0]);
while(current < integers[0]){
current++;
publishProgress(current,integers[0]);
SystemClock.sleep(1000);
if (isCancelled()) break;
}
return current;
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
tv.setText("执行完毕");
bt.setEnabled(true);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (pb.getMax()!=values[1])
pb.setMax(values[1]);
pb.setProgress(values[0]);
}
@Override
protected void onCancelled(Integer integer) {
super.onCancelled(integer);
tv.setText("执行取消"+integer );
bt.setEnabled(true);
}
}
执行效果如下:
可见AsyncTask在执行耗时后台任务和及时更新前台的处理上还是很不错的,下面我们就来分析一下源码。本文源码基于Android 8.0。源码位置:
android\frameworks\base\core\java\android\os\AsyncTask.java
首先从构造函数看起:
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() {
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(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);
}
}
};
}
一共有三种构造,但是后两种都是隐藏的,我们只能调用第一种,但前两种都间接的调用了第三个构造。起始构造函数并不复杂,工作就是初始化3个变量:mHandler ,mWorker ,mFuture 。我们一个一个来看。
-
- mHandler就是一个Handler。在AsyncTask中这个Handler的实现如下:
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:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
可以看到并没有什么特别之处,整个AsyncTask之所以前台能和异步任务交互也是归功于这个Handler
- 2.mWorker 的变量类型为WorkerRunnable,是一个实现了Callable接口的抽象类:
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
mFuture 是一个FutureTask类型的变量。这两个变量都是涉及多线程的东西,这里仅做一下简单介绍:
Runnable接口可以用来封装一个异步运行的任务,但是它没有返回值。所以又引入了Callable机制,它是有返回值的。Future则是为了配合Callable的,主要是维护线程的运行和状态。但是Future需要实现的方法太多,,所以又给我们提供了更加简洁的类FutureTask,而且他接受一个Callable类型变量后可以转换为Future或Runnable。
mTaskInvoked和mCancelled都是原子类型的布尔变量。在mWorker 中我们发现这里调用了doInBackground方法,这也就是这个方法在后台运行的原因。
我们在启动一个异步任务时都会调用execute方法,我们接下来从这里入手
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask 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;
}
首先execute内调用了executeOnExecutor,真正开始异步任务的地方在这里。这里面首先判断了一下状态,若正在执行或已经结束就抛出异常,这也就是为什么AsyncTask只能执行一次的原因。之后若没有执行过,这设置状态位为RUNNING。接下来调用onPreExecute();然后向mWorker中传递参数。最后调用exec.execute(mFuture);这里的exec就是sDefaultExecutor,接下来看sDefaultExecutor:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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 run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
可见sDefaultExecutor的execute方法实际上是将Runnable 放入ArrayDeque队列,然后初始时mActive为null,然后调用scheduleNext从队列中取出一个Runnable 放入线程池执行,这个线程池实现如下:
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
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;
}
如果不清楚线程池的话,可以参考我这篇文章。
简单分析一下这个线程池:
- 1、核心线程数是根据CPU核心数定的,但是在6.0上简单定义为最大核心数加一。在8.0版本上定义为不小于2且不大于4。在更早的版本上直接写为某个固定值,如5。可见Google对于线程池大小也没有一个一贯的处理方法。
- 2、最大线程数为核心线程数的两倍加1
- 3、核心线程外的线程存活时间为30s
- 4、阻塞队列类型为LinkedBlockingQueue,也就是链表型的。
- 5、线程工厂就是简单的对线程命了一个名
- 6、任务拒绝策略为默认的,即AbortPolicy,丢弃任务并抛异常。
看到线程池可能有些人有疑问,既然一个AsyncTask对象只能执行一次,为什么要建立线程池,其实虽然一个对象只能执行一次,但是我们可以实例化多个对象,分别执行,THREAD_POOL_EXECUTOR和SerialExecutor 都是static类型的,所以体现了线程池的价值。
到这里任务就已经启动了。中间调用了onPreExecute和doInBackground两个方法。那么其他方法在哪调用呢?还是回头看mWorker的定义,在try语块(语块内就是调用doInBackground地方)的finally语块中调用了postResult(result);
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
private static class AsyncTaskResult {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
上面就是相关代码。首先postResult中发送一个消息,Handler收到后,调用finish,finish根据情况调用onCancelled或onPostExecute最后修改状态。
接下来看onProgressUpdate方法,这个方法只有我们在调用publishProgress时才会调用。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
可见还是利用Handler,间接调用onProgressUpdate。
最后看cancel方法,
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
public final boolean isCancelled() {
return mCancelled.get();
}
这是AsyncTask比较有意思的地方。可见他只是调用了mFuture的cancel方法:
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
若mayInterruptIfRunning为false时什么都不做,为true时调用Thread 的interrupt方法。
由于java设计者认为并不能粗暴的让一个线程去毫无预兆的停止另一个线程,所以废弃了stop等方法,保留的interrupt方法实际上并不能有效的保证线程关闭。调用interrupt后,有以下两种结果:
- 1、如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常
- 2、如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。
也就是说interrupt仅仅是告诉线程,该停止了,但是停不停是由线程自己决定。可见AsyncTask的取消功能并不能真正停止任务,只是改变mCancelled状态,让最后finish方法中走到onCancelled上而已。那么问题来了,如果注释掉我开头例子中doInBackground的if (isCancelled()) break;这一句,仅仅调用cancel方法,是不会立刻回调到onCancelled的,可以验证:
可见虽然调用到了onCancelled方法,但是doInBackground的内容还是执行完了(后面的数字5是doInBackground的结果)
若要真正实现停止可以像我例子中那样在doInBackground适当的做控制,检测到取消时就退出doInBackground,或者在Thread中做控制,检测interrupt标志位。
到这里源码基本就分析完了,简单总结一下,AsyncTask本质上还是Handler+Thread的模式。并且引入了线程池,Handler主要是工作在主线程中进行各种方法的回调。源码中涉及的多线程技术还是值得我们学习的。