前段时间我有说过HandlerMessageLooper三者之间的关系,它们可以解决线程间通信,可以用作子线程处理耗时操作,主线程刷新UI等。今天我要说的是另外一种解决方案:AsyncTask。它的内部还是使用异步消息处理机制,只是Android对这三者进行了高度的封装。如果你对异步消息处理机制还不是很清楚的话,你可以看看这篇文章:一天一Android之异步消息处理机制。来看看AsyncTask到底如何使用。

基本用法

首先看一下AsyncTask的基本用法,因为AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它,在继承时,我们可以为AsyncTask类来指定3个泛型参数,这3个参数的用途如下:

  • Params 在执行AsyncTask时需要传入的参数,可以在后台任务中使用
  • Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为返回值类型
  • Result 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

所以,我们定义一个AsyncTask就可以写成如下方式:

public class DownloadTask extends AsyncTask<Void,Integer,Void> {
...
}

这里,我们指定第一个泛型参数为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务,第二个泛型参数我们指定为Integer,表示使用整型数据来作为进度显示单位,第三个泛型参数指定为Void,表示不指定反馈执行结果。

一般情况下,我们需要重写AsyncTask中的几个方法才能完成对任务的定制。经常需要重写的方法有四个:

onPreExecute()

这个方法会在后台任务开始执行之前调用,一般我们会在这里进行一些初始化,比如显示一个进度条对话框等。此方法执行在主线程中。

doInBackground(Void… params)

这个方法中的所有代码都在子线程中执行,应当在这里进行耗时的操作,一旦完成操作,可以使用return语句来将执行的结果返回,因为我们这里定义的返回类型是Void,所以我们这里可以不返回或者返回null。另外特别注意,在这个方法中是不能进行UI操作的,如果需要更新UI,比如更新下载进度等,需要调用publishProgress(Progress... values)来完成。

onProgressUpdate(Integer… values)

当我们在后台任务中调用了publishProgress(Progress... values)方法后,这个方法就会被执行,并且执行在主线程中,而且此方法所带的参数正是在后台任务方法中传多来的,我们一般再这个方法中更新UI,比如更新下载进度等。

onPostExecute(Void aVoid)

当后台任务执行完成并且通过return语句返回时,这个方法就会被调用,返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些操作,比如关闭进度条对话框等。当然,此方法执行在主线程中。

我们模拟一个下载任务:

public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity";
private ProgressDialog mProgressDialog;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textView);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMax(100);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
//启动任务
new DownloadTask().execute();
}
public class DownloadTask extends AsyncTask<Void,Integer,Void> {
@Override
protected void onPreExecute() {
//显示进度条对话框
mProgressDialog.show();
Log.d(TAG, "onPreExecute: "+Thread.currentThread().getName());
}
@Override
protected Void doInBackground(Void... params) {
//模拟耗时操作
for (int i=0;i<100;i++) {
try {
Thread.sleep(80);
}catch (Exception e) {
e.printStackTrace();
}
//更新下载进度
publishProgress(i);
}
Log.d(TAG, "doInBackground: "+Thread.currentThread().getName());
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
//主线程更新进度对话框
mProgressDialog.setProgress(values[0]);
Log.d(TAG, "onProgressUpdate: "+Thread.currentThread().getName());
}
@Override
protected void onPostExecute(Void aVoid) {
//主线程隐藏进度对话框
mProgressDialog.dismiss();
mTextView.setText("加载完毕");
Log.d(TAG, "onPostExecute: "+Thread.currentThread().getName());
}
}
}

如果想启动一个任务,只需要调用如下代码即可:new DownloadTask().execute();

运行结果如下:



内部实现

那么,AsyncTask内部是如何实现的呢?那么,就从我们的执行异步任务的起点开始,看看execute方法:

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();
//保存参数,并设置Handler回调
mWorker.mParams = params;
//将mFuture添加到线程队列中
exec.execute(mFuture);
return this;
}

这段代码的意思是:首先会判断当前任务是否已经在执行,如果已经在执行则抛出异常,然后设置当前任务的状态为运行状态,调用onPreExecute()方法,一般我们都会重写此方法,会进行一些初始化操作,当前方法还是在主线程中。然后保存参数,执行exec.execute(mFuture),下面我们来看看mWorkermFuture

先来看看mWorker这个成员变量所属的类WorkerRunnable

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

可以看到,它只有一个成员变量,就是存储我们传入的参数的,并且它还是一个抽象类,并没有实现Callable接口的方法。我们再来看看AsyncTask的构造方法:

public AsyncTask() {
//创建mWorker 并实现call方法,在合适的时间会调用call方法
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);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
//后台任务结束,处理返回的结果
postResult(result);
}
return result;
}
};
...
}

这里会对mWorker进行初始化。这里实现了call方法,并且设置mTaskInvoked=true,然后调用doInBackground(mParams),之后又调用了postResult(result)方法来操作后台任务返回的结果,那mWorkercall方法什么时候被调用呢?我们等会再来看,先来看看postResult()方法的实现:

private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//通过getHandler()方法获取Handler对象 然后创建Message对象
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
//异步发送消息
message.sendToTarget();
return result;
}

终于,我们看到了我们熟悉的异步消息机制,这里传递一个messagemessage.whatMESSAGE_POST_RESULTmessage.objectnew AsyncTaskResult<Result>(this, result),我们再来看AsyncTaskResult:

private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}

AsyncTaskResult只是一个简单的携带数据的对象。

看到这里,我们一定相信在某处还存在一个Handler。在postResult(Result result)方法中,通过getHandler()方法获取了一个Handler对象,我们再来看这个方法:

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

这段代码是线程安全的,并且对sHandler进行了初始化。通过查看sHandler,它是一个静态变量,所以我们可以得出,在整个AsyncTask体系中,都只存在一个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;
}
}
}

InternalHandler类重写了handleMessage方法,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]),其实就是执行了AsyncTask.this.finish(result),在接收早MESSAGE_POST_PROGRESS消息时,执行了result.mTask.onProgressUpdate(result.mData),其实是执行了AsyncTask.this.onProgressUpdate(progress),我们先看finish方法:

private void finish(Result result) {
if (isCancelled()) {
//如果是取消 回调onCancelled
onCancelled(result);
} else {
//正常结束任务 回调onPostExecute
onPostExecute(result);
}
//设置当前任务的状态
mStatus = Status.FINISHED;
}

可以看到,如果我们调用了cancel(),则执行onCancelled()回调。正常情况下调用的onPostExecute(),而我们一般都会重新onPostExecute()方法。

现在我们有个疑惑,那么后台任务在什么时候执行呢?那就要看mFuture了。再看AsyncTask的构造方法:

public AsyncTask() {
...
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);
}
}
};
}

任务执行结束会调用postResultIfNotInvoked(get())get()表示获取mWorkercall方法的返回值,即Result,然后看postResultIfNotInvoked方法:

private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}

如果mTaskInvoked不为true,则执行postResult,但是在mWorker初始化时就已经将mTaskInvokedtrue,所以一般这个postResult执行不到。

好了,现在mWorkermFurture都已经介绍完了,不过这里只是初始化了两个对象,并没有真正的执行,下面看真正调用执行的地方。

不知道大家还记得不,在executeOnExecutor中有这样一句代码exec.execute(mFuture),而exec又是传过来的,为sDefaultExecutor。下面来看看这个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);
}
}
}

可以看到sDefaultExecutor其实为SerialExecutor的一个实例,其内部维持一个任务队列,直接看其execute(Runnable runnable)方法,将runnable放入mTasks队尾。并且判断当前mActive是否为空,为空则调用scheduleNext方法,scheduleNext放则直接取出任务队列中的首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。

再来看THREAD_POOL_EXECUTOR是什么:

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

可以看到就是一个自己设置参数的线程池,参数为:

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

看到这里,大家会认为背后原来有一个线程池,最大的并发数,长度等和CPU都有关系。但是,其实这是一个线性执行任务的线程池,大家再看SerialExecutor,在execute方法中,会判断当前mActive是不是空,如果不是空,是不会取下一个任务的,只有当前任务执行完成,才会取下一个任务。

再来看mFuture,在创建mFuture的时候就已经将mWorker传给了mFuture,而mFuture又是间接继承了Runnable类,所以,mFuture会在run方法中去执行mWorkercall方法:

先来看FutureTask类:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
//记录回调
this.callable = callable;
//设置当前状态
this.state = NEW; // ensure visibility of callable
}

构造方法中,记录了当前的callable也就是mWorker。我们直接看run方法:

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 {
//调用mWorker 的 call方法 执行后台任务
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);
}
}

我们可以清楚的看到,这里执行了mWorkercall方法。

还有一个方法我们没说,publishProgress:

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

很简单,直接使用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:
//耗时操作完成
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//进度更新
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}

总结

好了,至此AsyncTask的使用和原理我们已经说完了,再总结一下原生:

  • 首先AsyncTask内部维护了mWorkermFuturemWorker是真正的执行后台任务,mFuture是一个线程类,内部持有mWorker对象,主要作用是让mWorker去执行后台任务
  • 并且AsyncTask类内部维护一个静态的线性的线程队列SerialExecutor,只要调用AsyncTaskexecute方法,就会将mFuture加入到线程队列中,然后开始执行队列中的任务。
  • 另外AsyncTask内部还维护了一个静态的Handler,当mWorker任务执行完成后,会使用Handler开启异步消息机制发送消息,并且当更新进度时,也会发送消息。

好了,关于AsyncTask我们已经了解的差不多了。