从源码的角度了解AsyncTask

可能最近时间比较紧张,没有足够的时间去学习新东西,那就利用这紧张的时间再来复习AsyncTask吧。

首先我们应该清楚AsyncTask主要用来实现什么业务,有什么优点,为什么要用它对吧。

诉求:
1. 我们知道,在Android中,如果执行耗时操作,一定不能在主线程中执行,而且更新UI界面又必须在在主线程中操作。
2. 如果耗时操作比较多,很多人一般情况下就会开启很多子线程去执行耗时任务,所以这样一来,这就给系统带来非常大的负担,随之而来就影响了整个系统和整个App的性能。
3. 有时候我们在很多业务场景中,需要启动很多线程,那么线程与线程之间的交互,线程之间的通信。

所以针对上面三条诉求(严格来说还有其他的一些诉求),我们起初会更愿意使用AsyncTask来实现满足这样的诉求,所以AsyncTask不仅可以有效管理线程数量,任务排序调度,还能实现线程之间的通信,还有有效解决android中的ANR,以及UI界面的更新问题,AsyncTask做了这一些列的封装。

public abstract class AsyncTask<Params, Progress, Result> {....}

所以很明显,AsyncTask是一个抽象类,必须继承派生出来一个子类才能使用。

AsyncTask定一个了三个比较有意思的泛型参数:
Params:启动Task的时候需要传入,也就是说调用AsyncTask对象的execute(Params)传入。

@Override
 protected String doInBackground(Integer... integers) {
   return null;
 }

Progress: 后台执行的一个百分比值(非严格来说就是这样的)。

@Override
protected void onProgressUpdate(Integer... values) {
 super.onProgressUpdate(values);//可以更新一些进度信息或者进度条
}

但是这里需要注意的是,onProgressUpdate(Progress)方法是通过AsyncTask内部一个final修饰的方法publishProgress(Progress)调用的。

@WorkerThread
 protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
      getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
      new AsyncTaskResult<Progress>(this, values)).sendToTarget();
     }
 }

最后通过发送一个Handler消息MESSAGE_POST_PROGRESS,给Handler,我们再看这个Handler如果合理这个消息的(我们先看流程,不要忽略Handler是子线程的还是主线程的,但是能更新进度界面,那肯定是主线程咯)。

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;
   }
 }
}

所以大家看到了吧,最后调用了onProgressUpdate方法,然后,所以我们派生继承子类的时候,把这个方法覆盖实现就能处理我们自己业务逻辑上的进度更新,因为publishProgress是一个final类型的,无法被Override。

Result:这是后台任务执行完毕后,输出给UI界面的数据,最后后台任务执行完毕,会通过调用onPostExecute(Result)输出给UI线程,所以这个方法执行是在主线程的。

@Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }

OK,上麦年我们分别简单说了一下三个反泛型参数的意思。
那么接下来我们再说说几个功能性的成员方法吧。

//在执行后台任务之前可以在这个方法中做一些事情(主线程)
@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) {
}

//取消结束一个任务,调用方法之后,会在之后调用onCancelled(Result)方法
public final boolean cancel(boolean mayInterruptIfRunning) {
  mCancelled.set(true);
  return mFuture.cancel(mayInterruptIfRunning);
}


//当用户调用取消之后回调,在主线程中执行
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
    onCancelled();
}

OK,通过在代码中加注释分别介绍了几个方法的功能和使用场景,相信大家都应该能理解。

那既然也介绍了那么多,那就顺带也来写个小Demo,模拟一下下载任务吧。

package com.zhg.views.asynctask.demo;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
    private TextView mTextView;
    private DonwnloadAsyncTask mDownloadAsyncTask ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDownloadAsyncTask = new DonwnloadAsyncTask();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //启动任务,传入一个数字100的int类型参数
        mDownloadAsyncTask.execute(100);
    }

    @Override
    protected void onPause() {
        super.onPause();
        //当Activity进入onStop的时候调用cancel
        mDownloadAsyncTask.cancel(true);
    }

//派生继承AsyncTask
private class DonwnloadAsyncTask extends AsyncTask<Integer,Integer,String>{
 @Override
 protected String doInBackground(Integer... integers) {
    if(integers!=null && integers[0] !=null) {
        for(int i  = 0 ;i < integers[0] ; i++){
//把进度通过publishProgress方法提交到主线程中的onProgressUpdate
            publishProgress(i+1);
            try {
                Thread.sleep(1000);//模拟下载的延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    return "执行完毕";//后台任务处理完毕,返回处理结果
 }

  @Override
  protected void onProgressUpdate(Integer... values) {
      super.onProgressUpdate(values);
      if(values!=null && values[0]!=null){
          //在这里更新进度
          mTextView.setText(values[0]+"");
      }
  }

  @Override
  protected void onPostExecute(String s) {
      super.onPostExecute(s);
      mTextView.setText(s);//显示处理结果
  }

   @Override
   protected void onPreExecute() {
       //这个就是在任务启动先获取TextView
       super.onPreExecute();
       mTextView = (TextView) findViewById(R.id.textview);
   }
  }
}

Demo比较简单,其实主要是想让大家了解这个AsyncTask的用法。
OK,现在我们就可以开始跟源码来了解原理了。

OK,那就从execute(Params)开始跟代码。

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}

代码很简单,就一行代码,但是有一个很重要的细节,在调用executeOnExecutor(sDefaultExecutor, params),的时候,传递了一个sDefaultExecutor参数,这个参数是啥呢?来看看先。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

SERIAL_EXECUTOR 是一个Executor ,我们知道Executor 是一个线程执行者,SerialExecutor内部类继承了Executor。

private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
  public synchronized void execute(final Runnable r) {
  //mTasks.offer向队尾插入元素
    mTasks.offer(new Runnable() {
      public void run() {
        try {
            r.run();
        } finally {
          scheduleNext();//一个任务执行之后,立即安排下一个任务执行
        }
      }
    });
    //最开始mActive 为null,立即安排一个任务执行
    if (mActive == null) {
       scheduleNext();
    }
  }
   protected synchronized void scheduleNext() {
     if ((mActive = mTasks.poll()) != null) {
        //把一个任务交给线程池执行
         THREAD_POOL_EXECUTOR.execute(mActive);
     }
   }
 }

mTasks 很明显,是一个集合,是一个装着任务体的集合,Runnable。
上面源码的注释我都说清楚了,所以这sDefaultExecutor (SerialExecutor) 其实就一个用来安排一个线程到线程池中去执行,以及存储后台任务的角色。但是这里有一个比较重要的东西,那就是
THREAD_POOL_EXECUTOR,那么我们来看看THREAD_POOL_EXECUTOR这个玩意是什么吧。

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
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 ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
    public Thread newThread(Runnable r) {
    return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
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;
}

没错,THREAD_POOL_EXECUTOR就是一个活生生的线程池,创建了一个
大小为CORE_POOL_SIZE的线程池,这个的大小是多大?默认它是通过获取当前CPU的核数,根据CPU的核数来计算的,也就是说,当CPU核数小于4的时候,线程池为2,当CPU核数大于4个的时候,线程池的默认大小为4。
写个伪代码让大家理解理解:
int size = 2;
int cpuCount = ?;
if(cpuCount < 4){
size = 2;
}else if(cpuCount > 4){
size = 4;
}

MAXIMUM_POOL_SIZE 这个就是线程池最大的大小,是CPU核数的2倍加1。
然后在创建了一个任务队列sPoolWorkQueue,这个任务队列最大可以存放128个任务,大家注意,这个128不是线程池的大小,他只是表明这个队列可以存放128个任务而已,这个队列是一个先进先出的队列。

OK,到这里我们也就明白了,然后我们继续恢复到主线上来跟源码吧。

@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;
}

这个方法也很简单,先把状态标记为正在执行,然后调用onPreExeceute()方法,这个方法我们很熟悉,这个方法就是在主线程中执行的,用来在任务执行之前执行一些业务,然后把当前参数设置给了mWorker.mParams。
我们看看这个mWorker是个什么东西。

private final WorkerRunnable<Params, Result> mWorker;
....
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
   Params[] mParams;
}

我们发现WorkerRunnable是一个抽象类,而且这个抽象类同时还实现Callable接口,定义了一个成员变量Params[] mParams,没错,刚刚我们传进来的参数就是赋值给他了。
先看看Callable是个什么接口。

@FunctionalInterface
public interface Callable<V> {
        V call() throws Exception;
}

很简单,就一个call方法,返回result。
接下来我们看看mWork是在哪里赋值创建的呢?

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
    mTaskInvoked.set(true);
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
           //noinspection unchecked
           Result result = doInBackground(mParams);
           Binder.flushPendingCommands();
           return postResult(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 是以匿名内部类的方式创建的,然后细心的人可能已经注意到,在call方法中调用了我们特别重要的一个方doInBackground(params)方法,OK,我们先不管,我们继续会到主线跟代码。
然后就是调用exec的.execute(mFuture)方法,在一开始我们就介绍了AsyncTask默认的sDefaultExecutor ,这个就是用来存储任务,和安排一个任务到线程池去执行的,他不是直接的去执行任务,他最终他会交给线程池去调度一个空闲线程去执行,我们知道把线程池的execute方法,都接受一个Runnable对象,那么这里的mFuture其实就是一个Runnable对象,稍微看看吧。

private final FutureTask<Result> mFuture;

这个我们知道,在上面Async的Task方法中就已经实例化了这个对象,也是通过匿名内部类的方式创建的,Override了一个done方法,需要注意的是,这创建的时候,在它的构造方法中传递了mWorker对象进去了。

从这样来看,也没发现它是一个Runnable,我们继续刨根问题,再看FutureTask是个啥东西。

public class FutureTask<V> implements RunnableFuture<V> {
...
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);
      }
  }
}

到这里大家应该一目了然了吧,这个还真是一个Runnable对象,并且已经实现了run()方法,而且这个run方法里面还拿到我们传递给他的mWorker对象,即Callable c = callable,然后直接调用mWorker的call方法,我们前面分析过,mWorker是通过创建匿名内部WorkerRunnable的方式创建的,他实现了Callable接口,Callable有一个call方法,而我们也发现,在mWorker的call方法中调用doInBackground(…)方法,就这样,我们的后台任务业务代码,就这样在子线程中得以执行了。

OK,我们继续回到主线,我们把任务提交给了线程池执行之后,如果任务执行完了,怎么回调我们的onPostExecute(Result)呢?

我们这时候应该回到WorkerRunnable的call方法中去,发现doInBackground方法后面还有一行特别关键的代码,即

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
    mTaskInvoked.set(true);
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
           //noinspection unchecked
           Result result = doInBackground(mParams);
           Binder.flushPendingCommands();
           return postResult(result);
     }
    };
    ....
}

对,没错,就是postResult(result);很明显,从字面理解就是,提交处理结果。我们去看看这个方法怎么处理的,怎么把结果抛给主线程方法onPostExecute()方法中去的。

private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
    return result;
}
.....

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

现在我们的代码比较熟悉了把,直接创建了一个Message,我们都知道Mesage一般都是和Handler结合使用的。
终于我发现下一行代码就直接调用了getHandler()获取一个Handler对象来产生了一个Message对象,我们知道用Handler的obtainMessage方法缠身的Message对象,起内部的target默认就是当前这个Handler。
所以最后我们把处理结果进行了封装,封装成AsyncTaskResult,AsyncTaskResult中的mTask接收的就是构造方法中传递的this,而mData就是构造方法中传递过来的result,最后把消息发送了出去,那么无可厚非,我么那直接看Handler怎么创建和怎么处理消息的把。

private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

代码很简单,创建了一个静态的方法,静态方法中创建了一个InternalHandler对象,那么这个对象很明显就是一个Handler。

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;
   }
 }
}

所以到这里大家都很清楚了,Handler创建的时候,构造方法中传递的Looper是MainLooper,即主线程的Looper,所以onProgressUpdate和onPostExecute方法最终是在主线程执行的 , 最后调用了result.mTask.finish(result.mData[0]);
result 我们知道是AsyncTaskResult对象,mTask其实就是AsyncTask自己,最后调用了AsyncTask的finish(…)我们来看看把。

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

很简单最后,我就不说了,大家都明白。其实呢,到这里基本讲完了。
最后说说AsyncTask平时使用的一些细节。

我们知道,最新的AsyncTask,在很多方面都已经得到改进,比如,线程池的问题,我们就已经发现很多好处,不知道大家通过上面的分析, 有没有注意到,其实executeOnExecutor是一个public的方法,这个方法第一个参数接受一个Executor 的参数,这是一个非常好的有点,一个扩展点,如果那天AsyncTask内部默认维护的线程池我们满足不了我么你的需求,我们可以通过外界传递一个自定义的线程池。

另外还有注意几点:
1.AsyncTask的实例必须在UI thread中创建,并且execute方法必须在UI thread中调用,但其实我个人认为这也是一个缺点,如果我不在UI线程中创建,那onPreExecute()方法执行就是在子线程中了,但是往往onPreExecute方法很多业务逻辑都在UI线程。

  1. 不要自己手动的调用
    onPreExecute(),
    onPostExecute(Result),
    doInBackground
    onProgressUpdate(Progress…)这几个方法
  2. 该task只能被执行一次,否则多次调用时将会出现异常。