AsyncTask是Android提供的一个轻量级异步任务机制,使用AsyncTask可以方便的执行异步任务,并将结果更新到main thread。AsyncTask中是通过Handler机制来让work thread和main thread通信的。如果你对Handler还不了解的话,可以通过我的这篇博客来了解Android的Handler机制。Android 异步消息处理机制
在这篇文章中我们将了解AsyncTask的基本用法以及从源码的角度来分析AsyncTask机制,首先我们来了解下开发过程中AsyncTask的使用
AsyncTask类的基本方法:
public abstract class AsyncTask<Params, Progress, Result> {
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to {@link #execute}
* by the caller of this task.
*
* This method can call {@link #publishProgress} to publish updates
* on the UI thread.
*
* @param params The parameters of the task.
*
* @return A result, defined by the subclass of this task.
*
* @see #onPreExecute()
* @see #onPostExecute
* @see #publishProgress
*/
@WorkerThread
protected abstract Result doInBackground(Params... params);
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
@MainThread
protected void onPreExecute() {
}
/**
* Runs on the UI thread after {@link #doInBackground}. The
* specified result is the value returned by {@link #doInBackground}.
*
* This method won't be invoked if the task was cancelled.
*
* @param result The result of the operation computed by {@link #doInBackground}.
*
* @see #onPreExecute
* @see #doInBackground
* @see #onCancelled(Object)
*/
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
*
* @param values The values indicating progress.
*
* @see #publishProgress
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
}
从上面AsyncTask的代码可以看到,AsyncTask是一个抽象类,我们如果想要使用的话必须先创建它的子类,并实现它的抽象方法doInBackground,根据功能需要我们还可以重写onPreExecute、onPostExecute、onProgressUpdate。从注释我们就可以知道onPreExecute、onPostExecute、onProgressUpdate这几个方法是运行在main thread,doInBackground是运行在workthread中的。我们把耗时的操作放在doInBackground进行,当doInBackground执行完之后会把结果返回给onPostExecute,我们可以在onPostExecute做一些更新UI的操作。onPreExecute在doInBackground之前执行,用于执行准备工作,onProgressUpdate用来更新后台任务的执行进度。
AsyncTask有三个泛型参数,分别是Params, Progress, Result。Params是指后台任务运行的参数,用于在doInBackground使用,Progress是用来指示后台任务执行进度的单位,Result是后台任务的返回结果,在doInBackground方法中返回,交给onPostExecute处理。这些参数不需要时可以用Void代替。
在本文中我们自定义一个简单的AsyncTask类,代码定义如下:
public class TestAsyncTask extends AsyncTask<Void, Integer, Void> {
private String taskName;
public TestAsyncTask(String name) {
super();
taskName = name;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Log.v("stone", "task = " + taskName + " onPostExecute in " + Thread.currentThread().toString());
}
@Override
protected Void doInBackground(Void... params) {
Log.v("stone", "task = " + taskName + " doInBackground in " + Thread.currentThread().toString());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.toString();
}
return null;
}
}
//AsyncTask的两种启动方式
new TestAsyncTask("task_" + i).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new TestAsyncTask("tasktesk_" + i).execute();
到目前为止我我们已经大致了解了AsyncTask的使用,以及启动后台任务的两种方式,execute和executeOnExecutor。在executeOnExecutor中我们使用了AsyncTask自定义的THREAD_POOL_EXECUTOR,它是一个ThreadPoolExecutor。这两种后台任务的启动方式有什么区别呢?一个应用中最多可以new多个AsyncTask呢?多个任务是并行处理还是串行处理呢?最多同时有多少个任务在处理呢?下面我们将从源码的角度来回答上面的问题,由于AsyncTask从出现到现在已经有了多次改动,下面我的分析将是基于API 23的源码来进行的,请各位注意不同API版本的AsyncTask实现是有差异的。
通过上文我们已经知道了AsyncTask的简单用法,下面我将从源码的角度来分析,一个AsyncTask从创建到执行的过程。首先一个任何AsyncTask子类的创建都会调用AsyncTask的默认构造函数AsyncTask(),下面我们来看一下这个方法的定义:
public AsyncTask() {
mWorker = new WorkerRunnable() {
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(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 和mFuture 这两个对象,我们暂时先不管立面的具体实现,只要知道这个mFuture 会交给Executor去执行。现在AsyncTask对象已经创建好了,我们来看AsyncTask的执行过程。我们先来分析execute()方法,该方法的定义如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute的代码实际上直接调用了executeOnExecutor方法,并且传入了一个叫做sDefaultExecutor的Executor对象。接着我们来看下executeOnExecutor方法的定义:
public final AsyncTask 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;
}
在executeOnExecutor中,我们先调用了onPreExecute()方法,这就解释了为什么onPreExecute方法是最新被调用的。我们把运行参数params赋给mWorker对象,mWorker实现了callable接口。把mFuture交给我们传入的Executor来执行。对面我们前文提到了AsyncTask的两种启动方式,他们执行方式的不同是因为传入的Executor不同导致的。我们前面提到过execute方法传给executeOnExecutor的是一个sDefaultExecutor对象,通过源码来看一下sDefaultExecutor是什么:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
我们看到sDefaultExecutor 被赋值为SERIAL_EXECUTOR,我们来看一下SERIAL_EXECUTOR是如何定义的:
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
SERIAL_EXECUTOR 是一个SerialExecutor对象,我们来看一下SerialExecutor的实现:
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
}
}
}
到这里我们可以知道,通过execute启动AsyncTask任务,会把任务一个一个添加到mTasks中,在执行完一个任务后才会去执行下一个任务,mTasks的大小没有限制,所以理论上通过execute启动AsyncTask任务这种方式可以创建无数个task,并且所有的task是串行执行的。这个runnable参数就是我们在AsyncTask构造函数中初始化的mFuture。我们接着再来看一下构造函数的源码:
public AsyncTask() {
mWorker = new WorkerRunnable() {
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(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方法,执行完doInBackground之后会调用postResult(result)来传递doInBackground的返回结果。我们来分析下postResult方法看看结果是如何传递的。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
postResult方法中主要是生成了一个MESSAGE_POST_RESULT消息,并将消息发送给了target handler。现在我们就要来看看这个Handler对象是什么,就知道是把消息发送到了哪个线程中。我们来看一下getHandler()方法的实现:
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
看到这里,我们知道这个handler是一个InternalHandler对象,我们接着来看一下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;
}
}
}
看到这里已经很清楚,这个handler是绑定了主线程loop的handler,所以接下来的工作就从工作线程切换到主线程中去执行。在handleMessage中可以看到MESSAGE_POST_RESULT会导致调用result.mTask.finish(result.mData[0])。这个就是AsyncTask的finish方法,我们来看一下finish方法的实现:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
finish方法里面会做判断,如果 isCancelled()返回的是true,就会去执行onCancelled,否则就会调用onPostExecute方法。至此我们就走完了execute方式的启动流程。通过以上的分析,我们可以知道这种方式下AsyncTask的对象可以创建任意多个并执行,且是顺序执行的。
对于executeOnExecutor启动方式,他的执行方式和传入的Executor相关,每个任务的执行逻辑和execute启动方式是一样的。executeOnExecutor方式中我们传入的参数是AsyncTask.THREAD_POOL_EXECUTOR,这是AsyncTask默认帮我们配置的Executor。我们来具体看下这个Executor的配置情况:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
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 sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
这个ThreadPoolExecutor配置如下:核心线程数为CPU核个数+1,最大线程数是2倍的CPU核个数+1,任务排队队列大小为128,所以假设我们的手机CPU核的个数为8,在核心线程数为9,最大线程数为17。所以根据ThreadPoolExecutor的特点我们可以知道,最大的任务数为128+17,超过这个数量就会抛出RejectedExecutionException。在排队队列未满之前,最多有9个线程在运行,当排队队列满了之后,最多有17个线程在运行。