结合Android 7.0源码,全面解析AsyncTask的源码,梳理AsyncTask使用过程中的一些注意事项。
分析源码之前,我们先来梳理一下使用,AsyncTask使用示例:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyAsyncTask().execute();
}
private class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... params) {
int downPercent=1;
publishProgress(downPercent);
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Void Void) {
super.onPostExecute(Void);
}
}
}
AsyncTask是一个abstract类,我们要使用AsyncTask类,需要实现它的抽象方法。AsyncTask的泛型参数有3个:
- Params 执行AsyncTask的时候传入的参数。
- Progress 后台任务的执行的进度参数。
Result 后台任务执行的返回值。
我们使用AsyncTask的时候会先构建一个AsyncTask的实例,然后调用它的execute方法,
然后会回调AsyncTask的4个方法,onPreExecute是执行在主线程中的,我们通常会做一些初始化的操作。 doInBackground方法中我们会做一些真正的耗时操作,如果是需要通知进度的话publishProgress通过进行更新,然后在onProgressUpdate方法中更新ui组件。 onPostExecute方法在doInBackground方法执行结束后回调。
使用起来很简单,但是我们要深究的话,就有很多疑问?比如:
1.为什么AsyncTask的几个方法可以在子线程和主线程之间灵活转换呢?
2.AsyncTask内部的实现原理究竟是怎样的?
带着这样的疑问,我们跟进源码来看一下:首先我们是构建了一个AsyncTask的实例,我们看一下AsyncTask的构造方法。代码如下:
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);
//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);
}
}
};
}
可以看到在构造方法中并没有执行什么代码,只是构建了两个对象mWorker ,mFuture 两个对象,然后把
mWorker 对象作为参数传递到了mFuture 对象中。这段代码看不出来什么。然后跟进execute方法中看一下:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
what… >–< 就一行代码,只能看executeOnExecutor方法了:
@MainThread
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;
}
这段代码才点有意思了呀,可以看到这里先执行了 onPreExecute()方法,
所以onPreExecute方法执行在哪个线程呢,一定就是主线程?no , no,no, 太绝对了。 应该是AsyncTask.execute方法执行在哪个线程它就执行在哪个线程。然后我们看到代码执行了exec.execute(mFuture)方法,这个execute是何许人也呢? 回头看一下我们的调用栈,发现exec其实是我们传递过来的sDefaultExecutor对象。从源码我们知道sDefaultExecutor是AsyncTask中的一个静态变量,我们来看一下赋值过程:
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);
}
}
}
赋值一通后,发现sDefaultExecutor其实就是一个SerialExecutor对象,那我们之前调用的sDefaultExecutor.execute方法,其实就是调用SerialExecutor的execute方法。来看一下execute方法的实现,可以看到这里是使用了一个ArrayDeque集合,将一个Runable对象压入到集合的尾部,但是这里有个比较精妙的地方,就是将Runable压入集合的时候,压入的是一个重新构建的Runable对象,然后在新的Runable的run方法中调用了传递进来Runable的run方法,而且finally方法块中执行了scheduleNext方法。
然后判断mActive是否为null,如果为null,就执行scheduleNext方法。 然后我们看一下scheduleNext方法,在这个方法中从ArrayDeque集合队首取一个Runable对象,然后丢进THREAD_POOL_EXECUTOR的
execute方法中。这里的这个THREAD_POOL_EXECUTOR又是什么呢?查看源码,我们可以知道,THREAD_POOL_EXECUTOR是一个线程池。
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
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;
}
分析到这里,我们需要梳理一下思路,才能进一步向下分析。从源码看到SERIAL_EXECUTOR对象是一个静态变量,意味着SerialExecutor对象在AsyncTask类中只有一份。那就是说ArrayDeque集合也只有一份。所以不论我们构建多少个AsyncTask的实例执行execute方法,最后都会放入同一个ArrayDeque队列中。结合我们之前Runable对象压入ArrayDeque和取出的方法。可以知道,不论我们当前应用构建多少个AsyncTask的实例并执行execute方法,都是顺序执行的。 这种方法其实是一种很有意思的单线程实现机制!
继续分析:我们现在知道FutureTask的run方法是执行在子线程的,按照逻辑doInBackground也应该运行在这个方法中。我们来找一下,我们注意到FutureTask的run方法中执行了callable.call方法:
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);
}
}
这里的这个callback对象就是我们构建AsyncTask的时候创建的mWorker对象,所以我们再回到看一下mWorker对象的call方法:
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;
}
};
果然,哈哈。doInBackground就在这里。所以doInBackground方法是执行在子线程中的。finally代码块中执行了postResult方法,跟进去看一下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
好眼熟呀,原来还是用了AsyncTask内部还是用了handler机制进行线程间的消息通信的,跟着源码我们很快就发现这个handler就是InternalHandler 对象。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@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;
}
}
}
原来postResult发送消息,然后在handleMessage中执行finish方法,然后我们看一下finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
先判断任务是否取消,如果任务已经取消就执行onCancelled,如果没有取消就回调onPostExecute,表示任务执行完成了。 既然onPostExecute是执行在handleMessage方法中,而这handler的looper对象又是Looper.getMainLooper()那么毫无疑问的onPostExecute就是执行在主线程中的。
然后我再看下publishProgress方法,
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
原来也是通过handler发送消息,然后在handleMessage中回调onProgressUpdate方法。
ok,分析到这里基本上我们就把AsyncTask的实现原理分析清楚了。 还记得前面我们说过现在的这个AsyncTask不论我们构建多少个AsyncTask实例执行execute方法,每个AsyncTask的后台任务(doInBackground)都是顺序依次执行的。 那有没有办法,让AsyncTask的后台任务并发执行呢?
当然,之前的分析我们知道因为AsyncTask是通过sDefaultExecutor来执行后台任务的,而sDefaultExecutor是SerialExecutor对象,SerialExecutor中的队列是顺序执行的,所以后台任务才会顺序执行。如果我把sDefaultExecutor替换为自己定制的线程池,不就可以实现并行执行任务了。
代码如下:
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
通过AsyncTask中的这个线程池,我们可以并行执行任务。 这个线程池创建的规则是什么??
分析完成AsyncTask的源码后,我们总结一下重要的知识点,以便之后使用的时候灵活应用:
1.AsyncTask.execute方法执行在哪个线程onPreExecute就执行在哪个线程。
2.不论我们当前应用构建多少个AsyncTask的实例并执行execute方法,所有都是顺序执行的。
3.如果需要并行执行AsyncTask任务,我们需要手动配置线程池。