### AsyncTask源码分析及个人理解
#### PS.阅读源码是开发者必备的能力,既然是能力那么就可以不断培养,逐渐增强的。
#### 一:整体概括
1.我们都知道AsyncTask在执行过程中的四个常用的方法:
```java
@MainThread
protected void onPreExecute() {}
@WorkerThread
protected abstract Result doInBackground(Params... params);
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {}
```
上述的四个方法,
onPreExecute是在task执行之前调用的。
doInBackground是在执行过程中调用,可以看到方法上标注的执行线程,这在工作线程中被执行。
onProgressUpdate是在task执行过程中,反映执行的进度的。执行的线程可以看出是在主线程中。
onPostExecute这个方法是task在执行完耗时任务之后,将结果回调的方法,可以看出这个方法也是在主线程中被调用的。
2.上述方法上标注的执行线程。有主线程 也有工作线程。在安卓开发过程中大家都知道安卓是单线程模式,也就是说安卓在执行过程中是在主线程中这个线程去处理关键任务,其他耗时操作应该放到子线程中去做。那么咱们就分析下这些线程是如何实现,以及怎么调用的。
在一开始查看AsyncTask源码时,对于顶部的实现就有线程池的生声明。注意,这些声明的变量时静态的,也就是说这是类共用的。
```java
//获取当前可用的cpu数量,这里有个疑问,如果正在执行任务的时候cpu忙碌,是不是会取到0?这是个问题
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//这个就是针对上边的这个疑问的兜底,反正至少有2个cpu可供使用
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;
//定义的线程池,真正干活就是下边定义的线程池
public static final ExecutorTHREAD_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;
}
//虽然这里又重新定义了Executor,但是内部调用还是上边声明的THREAD_POOL_EXECUTOR,除非是单独再设定执行的线程池。不过一般使用很少这么用吧。
public static final ExecutorSERIAL_EXECUTOR =new SerialExecutor();
private static class SerialExecutorimplements Executor {
final ArrayDequemTasks =new ArrayDeque();
RunnablemActive;
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);
}
}
}
```
好,上边的线程池已经定义了。回到上边的问题:如何控制是在主线程中执行还是在子线程中执行。我们都知道,AsyncTask必须在主线程中声明,并且必须在主线程中调用。为什么会有这样的要求,因为内部实现使用的handler就是执行线程中的looper,如果在子线程中执行,会直接报错,因为AsyncTask已经做了检查。
3.准备工作已经做好了,那么去看看如何调用:
```java
//在定义一个task之后,真正调用的方式如下:
task.execut();
可以看到内部的实现具体调用方法是
@MainThread
public final AsyncTaskexecuteOnExecutor(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()方法已经被调用了,因为当前是在主线程,所以方法调用也是在主线程中的。
onPreExecute();
mWorker.mParams = params;
//下边就是真正执行任务的地方了 exec是excutor,执行的参数是runnable.mFuture这个参数是在初始化的时候创建的,具体创建 下边分析。
exec.execute(mFuture);
return this;
}
//这是创建AsyncTask的时候 创建的mFuture,可以看出,他是对Runnable的包装。
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);
}
}
};
可以看到 当一个任务被放到线程池中执行。调用的是mFuture持有的mWorker的call方法。这个方法内部就是真正执行耗时操作。
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方法在此被调用。而此时线程是子线程。也就是我们常用的编写耗时操作的地方。当这个操作执行完之后,回调用postResult方法,这个方法是使用handler发送一个消息,因为handler是在主线程中创建,所以这个handler处理消息当然也就在主线程中了。这个消息没有直接调用onPostResult方法,而是调用了AsyncTask的finish方法。这个方法如下:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
}else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
会根据是否取消来调用对于的方法。正常来讲,调用onPostExecute方法,将结果告知被调用放。此次任务完成。
```
4.上述分析的只是一个task被执行完成。对于上述的线程池来讲,因为是静态的,所以使用方是所有的对象共用。也就是说创建多个task任务来执行,他们会被封装成runnable来保存在队列中一个一个被执行。在AsyncTask类的说明中可以看到,这个类是在执行小的耗时不长的任务比较高效,如果是计算型任务,或者是耗时长的操作,还是建议使用线程池来搞。
5.对于源码分析以及个人的思考,源码在某种程度上绝对是十分高效的。相对绝大多数的开发者来讲,使用源码或者针对源码的封装在性能上绝对是可靠的。学习源码可以让我们看到设计者的思想,和他们使用技术。对于日常开发很有帮助。