AsyncTask机制原理解析
Android为我们提供了2种方便的异步处理方案,Handler和AsyncTask,两种方式适合的场景网上一搜就知道了,但是为什么呢?这篇分析将为你揭晓答案。前面分析了Handler的机制原理,还不熟悉的可以看下Handler机制原理。通过本篇的学习,来了解AsyncTask的工作原理。本篇内容较多,本着结论先行,分析在后的原则来看。
一,使用
使用呢是十分的简单,用一个类继承AsyncTask,拿着这个对象,执行execute就可以开始执行后台任务了。还是找个例子吧
* 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);
这个例子是AsyncTask源码中注释提到的例子。我们看到AsyncTask有3个泛型参数,分别代表什么意思呢
* <li><code>Params</code>, the type of the parameters sent to the task upon
* execution.</li>
* <li><code>Progress</code>, the type of the progress units published during
* the background computation.</li>
* <li><code>Result</code>, the type of the result of the background
* computation.</li>
简单来说,第一个参数(是个可变参数)是传给后台任务用的,第二个参数是后台任务执行过程中更新进度使用,第三个参数是后台任务执行完的返回结果。除此之外,还有几个方法介绍一下
* <p>AsyncTask must be subclassed to be used. The subclass will override at least
* one method ({@link #doInBackground}), and most often will override a
* second one ({@link #onPostExecute}.)</p>
至少重写一个方法,doInBackground,这个方法用来执行后台任务的,运行在工作线程
@WorkerThread
protected abstract Result doInBackground(Params... params);
通常,我们还会重写一个onPostExecute,这个方法是将后台任务的结果post给UI线程,运行在UI线程
@MainThread
protected void onPostExecute(Result result) {
}
其实,另外还有2个方法我们需要关注下,这里将4个方法的注释都贴出来
* <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
* <ol>
* <li>{@link #onPreExecute()}, invoked on the UI thread before the task
* is executed. This step is normally used to setup the task, for instance by
* showing a progress bar in the user interface.</li>
* <li>{@link #doInBackground}, invoked on the background thread
* immediately after {@link #onPreExecute()} finishes executing. This step is used
* to perform background computation that can take a long time. The parameters
* of the asynchronous task are passed to this step. The result of the computation must
* be returned by this step and will be passed back to the last step. This step
* can also use {@link #publishProgress} to publish one or more units
* of progress. These values are published on the UI thread, in the
* {@link #onProgressUpdate} step.</li>
* <li>{@link #onProgressUpdate}, invoked on the UI thread after a
* call to {@link #publishProgress}. The timing of the execution is
* undefined. This method is used to display any form of progress in the user
* interface while the background computation is still executing. For instance,
* it can be used to animate a progress bar or show logs in a text field.</li>
* <li>{@link #onPostExecute}, invoked on the UI thread after the background
* computation finishes. The result of the background computation is passed to
* this step as a parameter.</li>
* </ol>
onPreExecute,在任务开始前的准备工作,运行在UI线程,比如准备进度条
onProgressUpdate,一般用在后台任务进行时,更新进度等动作,运行在UI线程
二,原理分析
分析之前,我们先说结论,AsyncTask是对线程池和Handler的封装,任务只能执行一次,多次执行报错,任务可取消,默认串行执行。
2.1,成员分析
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
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;
这里能看到,通过获取CPU的核数,来确定核心池数量和最大池数量,这个池是啥,应该就是线程池吧。
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);
这里有个ThreadFactory,其实就是生产线程的工厂嘛,BlockingQueue,看名字是个阻塞的队列,其实就是放我们要执行的任务的。
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
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;
}
先看Executor注释,可以用来执行并行任务。这里有个静态final的对象,看名字,没错,这就是线程池了。不过,这里不是说并行吗,别急,往下看
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);
}
}
}
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
这里还有个串行的Executor,是啥呢,看到关键的一行,通过THREAD_POOL_EXECUTOR去execute,到这我们似乎应该明白了,pool是默认用来执行异步任务的,那么这个SerialExecutor其实可以理解为一个容器,对接收到的任务暂存,串行交给pool去处理。这也验证了AsyncTask默认是串行执行的,当然了,你可以自己定制为并行执行。
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
2种类型的消息,不需要解释了吧。
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
用这个枚举来标识任务的状态,看注释,每个任务只能执行一次。
private static InternalHandler sHandler;
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
到这里,知道了吧,AsyncTask内部有一个UI线程的Handler。
AsyncTask是可以取消任务的,其实是使用了FutureTask和Callable。 这里介绍一下,Callable是个借口,与Runnable类似,不同的是Runnable没有返回值,Callable可以指定返回值。而关于FutureTask,看一下源码注释吧
* <p>A {@code FutureTask} can be used to wrap a {@link Callable} or
* {@link Runnable} object. Because {@code FutureTask} implements
* {@code Runnable}, a {@code FutureTask} can be submitted to an
* {@link Executor} for execution.
意思是说,FutureTask可以封装Callable和Runnable,并且可以让Executor执行起来。FutureTask有两个关键方法,done,表示任务执行完,cacel,表示任务取消。AsyncTask之所以能取消,其实就是通过FutureTask来取消的
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mWorker是实现了Callable的对象。看到这里,应该对AsyncTask的特点有了初步的认识,没关系,下面还会继续加深。
2.2,工作原理分析
先看一下AsyncTask的构造方法
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);
}
}
};
}
这里初始化了2个成员,mWorker和mFuture,内部的方法并不会执行。那么就看看execute方法吧
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
这里说明了,方法运行在UI线程。
@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;
}
一上来,先对status进行判断和初始化(这里是一个任务只能执行一次的实现),接下来,看到了onPreExecute,就是我们可能需要重写的方法。然后把params赋给了mWorker。exec.execute就是sDefaultExecutor执行execute。又因为
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
所以也就是SERIAL_EXECUTOR的execute了。
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);
}
}
}
这里我们前面分析过,是用了个容器,让任务串行执行。传给pool的是mActive,其实是对mFuture的封装,而mFuture初始化又是使用的mWorker,所以最终执行到了mWorker的call方法,也就构造方法中的第一个成员的方法。这里会执行doInbackground,这里是在工作线程执行的。执行完会得到一个result,在finally中post出去。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
Handler前面已经介绍过了,这里知道封装完Message,发送给UI线程即可。AsyncTaskResult是一个内部类
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
看一下handleMessage
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;
}
}
可以看到,POST_RESULT会调用finish方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
这里我们看到了熟悉的onPostExecute方法,参数是result。那么POST_PROGRESS是怎么处理的呢?我们知道,doInBackground可能需要较长的时间执行完,中间的过程想更新进度条怎么办呢?可以在doInbackground中调用publishProgress
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
如果任务没被取消,则通过Handler发送出去,那么接下来又到handleMessage了,看一下前面的代码,onProgressUpdate
@MainThread
protected void onProgressUpdate(Progress... values) {
}
看到这里,工作原理分析就结束了,我们已经能知道AsyncTask是对线程池和Handler的封装,任务只能执行一次,任务可以取消,默认串行执行(注意理解为什么有这些特点)