AsyncTask是一种轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
主要有4个核心方法,它们的含义如下所示。
- onPreExecute():在主线程中执行,在异步任务执行之前,此方法会被调用,一般用于做一些准备工作。
- doInBackground(Params…params):在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务进度,publishProgress方法会调用onProgressUpdate方法。另外此方法需要返回计算结果给onPostExecute方法。
- onProgressUpdate(Progress…Values):在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
- onPostExecute(Result result):在主线程中执行,在异步任务执行以后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。
AsyncTask在具体的使用过程中也是有一定条件限制的,主要有如下几点:
- AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程,当然这个过程在Android4.1及以上版本中已经被系统自动完成。在Android5.0的源码中,可以查看ActivityThread的main方法,它会调用AsyncTask的init方法,这就满足了AsyncTask的类必须在主线程中进行加载这个条件了。
- AsyncTask的对象必须在主线程中创建。
- execute方法必须在UI线程中调用。
- 不要在程序中直接调用onPreExecute、onPostExecute、doInBackground和onProgressUpdate方法。
- 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
AsyncTask是串行执行任务的,但是可以通过executeOnExecutor方法来并行地执行任务。
AsyncTask其实本质上是有2个线程池(SerialExecutor:用于任务的排序;THREAD_POOL_EXECUTOR:真正执行任务)和handler组成,下面来分析一下源码。
源码分析
为了分析AsyncTask的工作原理,我们从它的execute方法开始分析,execute方法又会调用executeOnExecutor方法,它们的实现如下所示。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
AsyncTask中的任务主要有三种状态PENDING、RUNNING、FINISHED,可以看到,只有当任务为PENDING的时候才会继续运行,并且直接转为RUNNING,通过后面的源码,还能看到当任务完成以后,会转成FINISHED,这就解释了第五点条件限制(为什么一个AsyncTask对象只能执行一次)
继续往下看,onPreExecute()
即我们上面提到的第一个核心方法,它是直接在源码中调用的,不需要我们写程序调用。 然后是mWorker.mParams = params
exec.execute(mFuture)
,这里出现了2个没提到过的mWorker和mFuture,我们得先知道这两个东西是什么。
首先是mWorker:
private final WorkerRunnable<Params, Result> mWorker;
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public interface Callable<V> {
V call() throws Exception;
}
好了,线索断了,我们得去找调用它的地方了。发现是在AsyncTask的构造函数中
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 {
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<Result>(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);
}
}
};
}
这个构造函数可以分成三块,mHandler、mWorker、mFuture的实例化,其他先不管,我们先看mWorker。
首先先看try块中的代码
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //设置子线程优先执行
result = doInBackground(mParams);
这里出现了第二个核心代码 doInBackground,发现其是一个抽象函数,需要我们自己实现
protected abstract Result doInBackground(Params... params);
然后是finally代码块中的postResult(result)
,其相关代码是
private Result postResult(Result result) {
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
这里就很眼熟了,我这里将相关代码都贴出来,一起分析
1、private Handler getHandler() {
return mHandler;
}
2、mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
3、private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
4、private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
5、private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
6、private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
我这里贴了5个地方的代码,一个个来看,主要就是为了解释这个handler是处在什么线程中。
第一块就是返回一个handler,没什么好说的。
第二块是上面AsyncTask的构造函数中的代码,是一个三元运算符,也很好理解。
第三、四则是实例化了这个handler,并且重写了handleMessage方法。
第五块则是该AsyncTask的finish方法(首先判断当前的asynctask是否被取消,若果没取消则执行onPostExecute(result),这个第四个核心代码,这时候数据已经回调了,到这里我们就差不多执行完了几个重要的方法了,然后再将asynctask的执行状态切换到FINISHED状态。)
第六块则是把当前从网络下请求的结果数据result保存到data中。然后通过message.sendToTarget,就是通过handler把message发送出去。这里不理解的可以先去补一下消息队列的原理。
至此,mWorker就分析完毕了,总结来说,mWorker中主要存储了我们重写的doInBackground方法,即需要在线程池中运行的代码。然后通过handler发送出去。
然后是mFuture,它是继承自FutureTask,并将mWorker作参数,FutureTask代码有点多,主要就是在其done()中会调用mWorker的call()方法。在done()中还调用了postResultIfNotInvoked方法。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
其实在mWorker的构造方法中,我们已经mTaskInvoked.set(true);
所以这里的postResult(result)一般执行不到。
OK,我们差不多大致了解了mWorker、mFuture大概是怎么用的,我们就可以继续看最上面的executeOnExecutor
方法中的exec.execute(mFuture);
是怎么运作的。
public interface Executor {
void execute(Runnable command);
}
OK,线索又断了,我们继续找exec的构造函数。我们发现调用这个executeOnExecutor时传入的参数是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);
}
}
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;
}
SerialExecutor 是一个有2个方法的内部类,即execute和scheduleNext是一个类中的,阅读的时候需要注意一下。那么这里一下子就涉及到了两个线程池,我们原来提到,一个是负责任务的排序,一个用于真正执行任务。那么带着这个去看,很容易发现SERIAL_EXECUTOR 是负责任务的排队的,它在execute方法中将runnable放入mTasks队尾,然后判断当前mActive是否为空,如果不为空在调用scheduleNext()方法。然后在scheduleNext中取出任务队列中的队首任务,如果不为null则赋值给mActive对象并传入THREAD_POOL_EXECUTOR进行执行。
而THREAD_POOL_EXECUTOR则是在静态代码块的时候就构造了,也是一个线程池。
那么现在的源码流程已经结束了,还有一个问题就是doInBackground到底是调用在什么线程中的。
我们还是得看一下FutureTask
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
}
这里只展示了几个重要的方法,可以看到,这里的run方法中有 result = c.call();
这个c就是我们的mWorker,这也解释了doInBAckgournd是运行在子线程中。