在系统的源代码中找到相关的文件 AsyncTask.java,在阅读该文件的注释说明,就可以大致明白该类的作用,以及其的基本用法,接下来我们来看一下该对该类的解释:
AsyncTask 使在UI 线程中的使用变得适当和简单,这个类允许执行后台的操作并且可以将结果发布到 UI线程而不需要去操作线程和handlers.
AsyncTask 被定义为一个 thread 和 handler 的帮助类,不需要构建一般的Thread的框架, AsyncTasks 理论上应该被用于一个简短的操作(至多几秒钟),如果需要长时间保持线程运行推荐使用java 库中java.util.concurrent包中的Executor,ThreadPoolExecutor,FutureTask 等api方法.
一个异步任务被定义为在后台运行的计算并且发送结果到UI 线程中,其定义了3个范型的参数:Params,Progress和Result,还定义了四个基本的方法:onPreExecute,doInBackground, onProgressUpdate,onPostExecute.
AsyncTask 必须被子类继承才能使用,子类必须至少覆盖一个方法:doInBackground,在使用中经常也需要覆盖第二个方法onPostExecute.
以下是一个例子:
private class DownloadFilesTask extends AsyncTask<URL,Integer,Long>{
protected Long doInBackground(URL... urls){
int count = urls.length;
long totalSize = 0;
for(int i = 0;i < count;i ++){
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress){
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
// 一旦被创建之后,使用就很简单
new DownloadFilesTask().execute(url1, url2, url3);
下面介绍三个范型的参数:
* Params:这个参数将被发送到执行体
* Progress:在后台操作期间,这个参数将被不断的发送出去
* Result:后台的运算的结果并非所有的参数类型都会被使用,如果要标记一个参数未被使用,简单的使用Void 即可.
private class MyTask extends AsyncTask<Void,Void,Void>{...}
当异步任务被执行之后,它将经历以下的四个步骤:
onPreExecute():在任务被执行之前在主线程中调用,这步骤通常被使用建立一个任务,例如在用户界面显示一个一个进度条,当然一般在此可以做一些初始化的工作.
doInBackground(Params… params):在 onPreExecute()完成执行之后在后台线程立即调用,这个步骤主要在后台线程执行耗时的操作,异步任务的参数将会传递到这一步,这个步骤必须返回计算的结果,并将结果传递到onPostExecute方法.在这一步中也可以使用publishProgress方法,去发布一个 或者多个的进度,这些值将会在onProgressUpdate 里发送在 UI线程.
onProgressUpdate(progress… values):调用完publishProgress方法后会在UI线程中调用
此方法,如果不掉用publishProgress方法,则不会走该步.执行的时间选择是不确定的,当后台计算还在执行时,这个方法通常被用于显示用户界面的进度,例如它通常被用于显示进度条的动画或者在文本区域显示log.onPostExecute(Result result):后台的任务运行完成之后在UI 线程中调用该方法,后台计算任务的结果传递到这作为该参数,即doInBackground的返回值.
取消任务
调用cancel(boolean mayInterruptIfRunning)可以在任何时候取消任务,调用该方法会导致isCancelled()返回true,在调用这个方法之后,onCancelled(Object) 这个方法将会被调用,而onPostExecute(Result)不会被调用,为了保证任务尽快的被取消,我们应该周期性的在doInBackground()方法中检测isCancelled()的值(例如在一个循环内).
线程规则
这个类必须遵守以下线程规则才能正常的工作:
- AsyncTask 类必须在主线程中加载,这个已经在android 4.1以上的版本自动完成了
- AsyncTask 类的对象必须在主线程创建
- execute()方法必须在主线程中调用
- 不要手动调用onPreExecute,doInBackground,onProgressUpdate,onPostExecute这四个方法
- 一个AsyncTask 任务只能执行一次,即只能调用一次execute()方法(如果第二次调用会抛出一个异常)
内存观察
- 在构造方法或者onPreExecute中设置成员字段,并且在doInBackground指向他们
- 在doInBackground中设置成员字段,并且在onProgressUpdate和onPostExecute中指向他们
执行顺序
在Android 1.6 之前,AsyncTask在后台线程是串行执行的,在Android 1.6的时候,采用线程池来处理并行的任务,在Android 3.0之后,为了避免并发错误,任务在单线程上面被执行,如果真的想要进行并发操作,可以调用AsyncTask的executeOnExecutor(java.util.concurrent.Executor, Object[])来处理并行任务.
源码分析
首先当定义完异步任务之后,新建一个异步任务对象,然后调用execute方法。
我们应该明白AsyncTask是一个抽象类:
public abstract class AsyncTask<Params, Progress, Result> {}
首先从execute()方法开始
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@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;
}
由上可以看出,调用execute方法实际是调用executeOnExecutor,在该方法里面,首先判断此时的
状态,状态是枚举,状态一般分为三种:
- PENDING : 指示任务还没有开始执行
- RUNNING : 只是任务正在运行
- FINISHED : 代表任务已经完成,即onPostExecute 已经完成
初始默认的mStatus的值为Status.PENDING,所以在最开始的时候就对AsyncTask状态进行判断,只有未执行过的任务才能执行。
接下来 便是将任务的状态设置为Status.RUNNING 状态,便开始调用AsyncTask 任务的第一个方法onPreExecute(),之后便是线程调用,首先将任务的参数传递给mWork,之后执行线程方法。
在这里就要 涉及到AsyncTask的构造方法,在此之前,我们先来了解两个基本的多线程编程的类:
Callable: 为接口提供了一个call()方法,实现该接口的类可以作为被其他线程执行的任务,但是该方法是有返回值的(当然对于Runnable 的 run()方法是没有返回值的).
FutureTask:表示一种抽象的可生成结果的计算,它表示的计算结果是通过callable来实现的,FutureTask将计算结果从执行计算的线程传递到获取这个运算结果的线程,可以通过get()方法来获取任务的计算结果,其中get()并不是立即就可以得到:如果任务已经完成运算的话,就会立即返回结果,否则任务就会阻塞,直到任务完成返回计算结果(当然也有可能发生异常),在FutureTask类里面有 一个done()方法,在任务线程执行完成后调用,所以程序员可以写一个FutureTask的子类来处理线程任务执行完成后的逻辑。注意 FutureTask在进入完成状态之后,将永远停留在这个状态上面(不会被再次执行),所以在AsyncTask里面,FutureTask只在构造方法里面初始化了一次,并不会在其他地方再次初始化,这也是为什么AsyncTask只能运行一次的原因(当然通过AsyncTask状态也可以保证只运行一次,否则会抛出异常),其中还有一个cancel()方法,用于取消该任务,实际上AsyncTask类的取消任务就是调用的该方法。
说了这么多,我们还是来看一下构造方法,来看一下任务的构成:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
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的call方法中,首先将mTaskInvoked设为ture,表明当前任务已经被调用过了,然后便是执行AsyncTask的doInBackground方法,接着将其返回值传递postResult方法,接下来我们来看postResult方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
在此方法里面发送一个MESSAGE_POST_RESULT的消息,接下来看处理该消息的地方:
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;
}
}
}
这是一个静态的类,为了能将执行环境切换到主线程,就要求sHandler在主线程中创建,由于静态成员会在加载类的时候进行初始化,所以这就变相要求AsyncTask的类必须在主线程中加载,否则同一个进程中的的AsyncTask都将无法工作。接收到这个消息之后,就调用AsyncTask里面的finish()方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果取消了,就调用onCancelled方法,否则就会调用onPostExecute方法,可以看到doInBackground的返回值就被传递到了onPostExecute方法。
还有就是关于FutureTask的done()方法里面的postResultIfNotInvoked(get())方法,可以看一下:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
说明任务完成之后,如果if条件成立,依旧会执行postResult方法,
查看FutureTask的源码可知在调用cancel()方法取消任务的时候会执行这个done()方法,在发生异常的时候同样会执行这一方法,所以最终的postResult方法依旧会被执行.现在我们回到之前FutureTask调用的地方,看看任务是如何被调用的.
exec.execute(mFuture);
对于此方法的调用,我们首先应该找到该线程池,该exec的值为 sDefaultExecutor,查看代码:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
// offer(E): 将指定的元素E在此deque队列的末尾
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
// poll():检索并移除此queue队列表示的队列的头部。返回null如果此queue队列为空
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
由以上的方法可以看出,AsyncTask是串行执行,在这里,FutureTask充当了runnable的作用,接着这个任务会交给SerialExecutor的execute处理,首先将FutureTask对象插入到任务队列当中(在其中FutureTask调用其的run()方法,该方法会调用callable的call()方法),如果此时没有活动的任务,就会调用下一个AsyncTask任务,所以此时的AsyncTask默认情况下是串行执行的。
为什么是串行的呢?你看,我们首先将任务放入队列,最开始的mActive是为null,所以会执行scheduleNext()这个方法,将mActive = mTasks.poll()赋值,但是但第二个任务来的来的时候,mActive并不是为null,所以它要等到r.run()方法运行完之后,运行Finally里面的scheduleNext()方法,开始下一个任务,所以是串行的。在AsyncTask中,存在有两个线程池和一个Handler,其中SerialExecutor 用于任务的排队,THREAD_POOL_EXECUTOR 用于执行真正的任务,InternalHandler用于将执行环境从线程池切换到主线程.
至此关于AsyncTask的源码解析就结束了,希望会有所帮助.
参考文献:
1. Android开发艺术探索
2. AsyncTask源代码浅析(一) http://blog.csdn.net/chunqiuwei/article/details/40405277