AsyncTask是android为咱们提供执行异步任务的一个轻量的类,能够用来处理耗时操做,而且可以很方便的将执行结果返回给主线程。本篇文章将会经过源码分析来介绍AsyncTask的内部实现原理。html
AsyncTask里面几个重要的成员变量变量分别为:java
名称 | 做用 | 建立 | 调用 | 备注 |
---|---|---|---|---|
THREAD_POOL_EXECUTOR | 真正执行任务的线程池 | 在静态代码块中被建立 | 在SerialExecutor线程池的scheduleNext方法中被调用 | 该线程成池的核心线程数量是根据手机cup核数-1肯定的 |
sDefaultExecutor | 内部建立队列用于储存异步任务 | 建立类的成员变量的时候被建立 | 在AsyncTask的execute()中被做为参数传递 | SerialExecutor类的scheduleNext方法中会将任务添加到THREAD_POOL_EXECUTOR线程池中执行 |
mWorker | 任务最终执行方法,其内部的call方法会调用doInBackground()方法 | 在AsyncTask有参构造中建立 | WorkerRunnable在FutureTask的run方法中被调用该类的call方法 | 其继承自Callable方法,通常配合FutureTask使用 |
mFuture | 在其内部会调用mWorker的call方法来执行任务 | 在AsyncTask有参构造中建立 | FutureTask在SerialExecutor类的execute方法中被调用 | 该成员变量被AsyncTask的executeOnExecutor()中传递到SerialExecutor中 |
sHandler | 用于将在结果返回到主线程 | 在AsyncTask有参构造中经过调用getMainHandler来建立 | 在postResult()中经过复用Message来调用 | InternalHandler类的Looper是主线程的Looper |
在分析AsyncTask以前咱们先看看他的构造,咱们在使用AsyncTask
常常使用空参构造的方式来建立该对象,这个构造方法内部会调用他的有参构造。首先有参会先根据是否有Looper来建立Handler。若是传入的Looper为空或者传入的Looper不是主线程的Looper,则调用getMainHandler()
来建立Handler;若是是主线程的Looper则以此Looper从新new一个Handler。当Handler建立完毕后而后在以次建立WorkerRunnable
和FutureTask
。下面为AsyncTask构造源码:android
public AsyncTask(@Nullable Looper callbackLooper) {
//建立Hanlder
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);
//获取异步执行结果
result = doInBackground(mParams);
//将进程中未执行的命令一并送往cup处理
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//将处理结果返回到主线程
postResult(result);
}
return result;
}
};
//FutureTask间接调用了WorkerRunnable方法的call方法
//来获取执行结果
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其实是InternalHandler
,mWorker
内部的call()方法会调用doInBackground
,try块无论执行结果如何,都会调用postResult()
来调用Hanlder发送消息,通知主线程最Ui更新操做。先有一个问题,call()方法是在哪里会被调用呢?实际上是在mFuture内部的run()方法中调用mWorker
他的call方法。具体代码读者能够自行查找项目源码,这里就很少说了。上面提到的mWorker、mFuture会在execute()
方法中被调用和传递,execute()是用于配置和启动任务的方法,下面为该方法的部分代码。数组
/**
*在主线程中执行
*可传入一个或多个参数
*/
@MainThread
public final AsyncTask。<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
复制代码
executeOnExecutor(sDefaultExecutor, params);
方法将参数params和sDefaultExecutor
传入该方法中,并返回一个AsyncTask。这个params咱们知道它是咱们传进来的参数,可是sDefaultExecutor是什么呢?它是一个线程池,是一个类的成员变量。bash
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
复制代码
既然咱们知道sDefaultExecutor是一个线程池,也就是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() {
//mTask.pll()删除队列中的第一个元素,并返回该元素的值
if ((mActive = mTasks.poll()) != null) {
//调用线程池执行异步
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
复制代码
从上面的代码咱们能够知道,SerialExecutor类中建立一个双端队列ArrayDeque
, 用于储存异步任务。他还有execute()
和scheduleNext()
方法,execute()内部调用了mTasks.offer
用于将传入的异步任务添加到队列中,而后在调用 scheduleNext()方法。scheduleNext()方法调用mTask.poll()
方法取出并删除第一个元素,最后将取出的元素放到线程池中。不知道读者有没有发现AsyncTask内部实际上是有两个线程池SerialExecutor
和THREAD_POOL_EXECUTOR
,其中SerialExecutor线程池主要是用于将任务添加到队列中,而任务真正的执行是在THREAD_POOL_EXECUTOR线程池中。async
要想知道执行结果是如何被传递到线程中,咱们先搞明白AsyncTask的执行过程。其实读者从上面的内容中或许能改猜到它的大概执行过程。其实它的执行过程也不复杂咱们能够结果下面这张图进行分析:ide
咱们在使用AsyncTask的时候会先建立对象,而后调用execute()方法传入参数执行任务:函数
//建立AcyncTask封装类
TestAsyncTask asyncTask = new TestAsyncTask();
//传入参数,执行任务
asyncTask.execute(5,6,7);
复制代码
咱们在经过上面操做执行任务的时候,其实AsyncTask内部作了一下几个操做:oop
executeOnExecutor()
中校验该任务是否在任务栈中执行、或者是否已完成过结合上面的执行流程图咱们知道,在通过上面7个步骤异步任务一个一个的在线程池中被完成。既然咱们知道了AsyncTask的大体执行过程,那么它是如何将执行结果返回到主线程呢?下面咱们将会来分析。
咱们知道doInBackground()
函数是咱们的任务具体执行函数。这个函数是在WorkerRunnable的call()函数中被调用,从上面的执行过程介绍中咱们知道call()方法是在FutureTask的run方法执行的时候被调用的。当call()方法在执行完doInBackground()方法获得结果后,会将该结果传递给postResult()
方法:
private Result postResult(Result result) {
//obtainMessage方法是从Message池中获取一个Message对象,避免重复建立。
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
//发送消息
message.sendToTarget();
return result;
}
复制代码
postResult()方法内代码也很简单,首先它会经过Hanlder(注:从文章开始部分咱们能够知道,这个Handler的Looper是主线程的Looper)在消息队列中获取一个Message对象,而后将结果和定义的标记包装到Massage中,最后在经过Message对象调用sendToTarget()将消息发出。既然消息发送出去了,那么消息是在哪里执行呢?答案是:在InternalHandler
类中的handleMessage()
中被执行。why?由于getHandler()获取的是Hanlder是咱们在文章开始介绍的构造函数中被getMainHandler()赋值的mHandler
,而getMainHandler()中返回的就是InternalHandler。既然咱们知道了消息在哪里被处理,那么咱们能够看一看它的具体处理逻辑:
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;
}
}
复制代码
handleMessage()内部有两个判断,若是标识是MESSAGE_POST_RESULT
则将结果传递给finish()
方法。若是标识是MESSAGE_POST_PROGRESS
则调用onProgressUpdate()
用于更新进度。下面咱们先看finish()方法的源码:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = AsyncTask.Status.FINISHED;
}
复制代码
finish()方法会判断是否取消了该任务,若是用户调用了cancel()
函数那么isCancelled()
返回的就是true,当用户取消了任务那么将会回调onCancelled(result)函数而onPostExecute()则不会调用,反之则会调用。
在分析handleMessage()方法的时候咱们留了一个小尾巴,MESSAGE_POST_PROGRESS
这个标记消息在何时发出的?在回答这个问题以前,咱们先回忆一下咱们在使用doInBackground()
的时候,是否有在其内部调用publishProgress()
函数来更新进入?回忆到这里答案就很明显了:经过Handler发生更新进度消息的操做是在publishProgress()
函数中完成的。下面为该函数的源码:
@WorkerThread
protected final void publishProgress(Progress... values) {
//若是任务没有取消,则发生消息更新进度
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
复制代码
从上面的源码咱们能够知道,更新进度的消息是在子线程中发送的,若是该任务没有被取消那么就能够发现消息。这种经过复用Message对象发送信息的方式对性能上有起到优化的做用。读者能够在文章结尾的参考连接中找到相关的介绍,笔者就不介绍了。
文章到这里对与AsyncTask的源码分析也就介绍完了。在AsyncTask中比较重要的成员变量为:WorkerRunnable、FutureTask已经两个线程池,可以真正理解AsyncTask的执行过程必定要搞明白他们几个的调用过程。最后感谢您能在百忙之中抽出时间阅读这篇文章,下一篇文章将会写一下HandlerThead和IntentService。