AsyncTask,异步任务,参照源码中的一段注解:
/**
* <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
* to perform background operations and publish results on the UI thread without
* having to manipulate threads and/or handlers.</p>
* <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
* and does not constitute a generic threading framework. AsyncTasks should ideally be
* used for short operations (a few seconds at the most.) If you need to keep threads
* running for long periods of time, it is highly recommended you use the various APIs
* provided by the <code>java.util.concurrent</code> package such as {@link Executor},
* {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
* <p>An asynchronous task is defined by a computation that runs on a background thread and
* whose result is published on the UI thread. An asynchronous task is defined by 3 generic
* types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
* and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
* <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
* **/
从注解中我们大概可以知道AsyncTask的几个特点:
- 更优雅、方便地使用UI线程
- 适用于短耗时操作,如果你打算让子线程工作较长时间,建议使用线程池如Executor、ThreadPoolExecutor、FutureTask这些APIs
- task执行时的三个参数:Params、Progress、Result,都能为void类型
- task执行的四个方法:onPreExecute()、doInBackground()(must)、onProgressUpdate()、onPostExecute()(should)
* <h2>Cancelling a task</h2>
* <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
* this method will cause subsequent calls to {@link #isCancelled()} to return true.
* After invoking this method, {@link #onCancelled(Object)}, instead of
* {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
* returns. To ensure that a task is cancelled as quickly as possible, you should always
* check the return value of {@link #isCancelled()} periodically from
* {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
- 从注解中,我们可以知道AsyncTask是如何cancel掉当前正在进行的任务的。调用cancel()方法可以将当前任务停止下来。调用该方法后,会导致isCancelled() return true;在我们线程在后台运行时,doInBackground()会一直在后台运行直到任务正常结束。在doInBackground中会调用isCancelled()来检查任务是否被取消。如果被取消,就会走到onCancelled()方法中;否则会一直执行,知道结束后调用onPostExecute()方法。
<h2>Threading rules</h2>
* <p>There are a few threading rules that must be followed for this class to
* work properly:</p>
* <ul>
* <li>The AsyncTask class must be loaded on the UI thread. This is done
* automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
* <li>The task instance must be created on the UI thread.</li>
* <li>{@link #execute} must be invoked on the UI thread.</li>
* <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
* {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
* <li>The task can be executed only once (an exception will be thrown if
* a second execution is attempted.)</li>
* </ul>
- task任务必须在主线程中实例化
- execute()方法必须在主线程中执行
- 不要手动调用几个回调方法
- task只会被执行一次,否则会抛出异常
* <h2>Memory observability</h2>
* <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
* operations are safe without explicit synchronizations.</p>
* <ul>
* <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
* in {@link #doInBackground}.
* <li>Set member fields in {@link #doInBackground}, and refer to them in
* {@link #onProgressUpdate} and {@link #onPostExecute}.
* </ul>
- 内存问题,asynctask不用担心同步问题,回调方法都是同步方法,有严格意义上的先后顺序
<h2>Order of execution</h2>
* <p>When first introduced, AsyncTasks were executed serially on a single background
* thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
* to a pool of threads allowing multiple tasks to operate in parallel. Starting with
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
* thread to avoid common application errors caused by parallel execution.</p>
* <p>If you truly want parallel execution, you can invoke
* {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with * {@link #THREAD_POOL_EXECUTOR}.</p>
- 最早以前,AysncTasks都是串行执行的,共用同一个background Thread。从Android 1.6开始,虚拟机中允许多个AsyncTasks并行执行。但是到了3.0时,由于并行运算遇到了一些问题,又将运行方式改成了串行计算,共用一个Bg Thread。如果你自己对处理线程并发很有信心,可以通过调用executeOnExecutor(THREAD_POOL_EXECUTOR)来并行运行该任务。
下面将介绍整个AysncTask代码的整体执行流程,让我们清楚地知道这个类是如何开启第一个子线程执行任务,如何不断地调用doInBackgound(),又是如何中断任务的。
执行AsyncTask的代码灰常简单,只消一句:
new DownloadFilesTask().execute(url1, url2, url3);
其中DownloadFileTask是我们自定义的任务。在这个execute()方法中,我们逐步找到工作子线程开始执行的地方。从代码中可以看出,execute()方法通过层层封装,最终在executeOnExecutor()方法中开始执行子线程
@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)");
}
}
//将AsyncTask任务设置为正在运行
mStatus = Status.RUNNING;
//执行onPreExecute()
onPreExecute();
//将构造方法中的变量传递给mWorker这个Runnable
mWorker.mParams = params;
exec.execute(mFuture);//开始执行mFuture这个任务
return this;
}
默认情况下,上面的exec就是我们的SerialExecutor线程池,里面在真正执行线程任务
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//offer方法表示向任务缓存队列中添加一个任务,mTasks代表了SerialExecutor这个串行线程池的任务缓存队列
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();//实际上就是mFutrueTak执行run(),run()中再执行mWorker.call()方法
} finally {
scheduleNext();
}
}
});
//开始时,mActive为null,前面没有被激活的线程,所以第一个工作子线程从这儿开始
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
1.任务执行前的准备工作
如果是串行执行,AsyncTask提供了两个方法,来启动任务执行:
(1)
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
(2)
@MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
如果是并行执行,AsyncTask提供了一个方法
(3)
@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);//方法(2)也就是直接执行了这个方法,没有前面的这些操作
return this;
}
上面有几处值得我们注意:
- execute方法在主线程中执行
- 如果此时线程处于running or finished状态,将会抛出异常
- 第一个方法为final 类型,第二个为static类型,第三个为final类型
第一个方法其实就是对第三个方法进行了一个封装,将第一个参数设置成了SERIAL_EXECUTOR,这个也就是SerialExecutor的一个final类型实例。上面已经提过。而第三个方做了一些初始工作(mStatus = Status.RUNNING;onPreExecute();
mWorker.mParams = params;)之后,会去执行第二个方法。真正开始任务。也就是说它们的区别基本上是是否带输入参数,是否用并发线程池执行任务。以上是完成任务执行前的准备工作。
开始执行任务
看上面的(3)方法中的任务执行代码
exec.execute(mFuture);//exec为线程池类型,mFuture为Runnable类型,后面分析
对于一般的串行执行,上面已经给出了具体执行内容
这里再重新写一遍:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;//待执行的任务
public synchronized void execute(final Runnable r) {
//推入一个新的任务,任务内容是我们提供的runnable(mFutrue)
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
//从mTask队列中poll一个任务并在并行线程池中执行
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
正是上面的offer/poll保证了串行执行顺序。而且归根结底,最后还是ThreadPoolExecute在执行任务。
3.任务状态回调
任务在执行的过程中,会有两个回调方法表示当前任务的状态
- protected abstract Result doInBackground(Params… params);
- protected onProgressUpdate(Progress… progress);
文章开头我们说过,AsyncTask在创建实例对象中会new 两个很重要的对象–mWorker、mFuture,通过上面的方法(1)(3)得知,我们线程池最终执行的就是mFuture这个实现了Runnable接口的对象。而此对象实际上是对mWork做了一个封装的,最终在mFuture的run()方法中执行了call()方法。之所以进行封装,完全是出于解耦的考虑:
- FutureTask(mFuture对应的类)是对任务执行的状态进行了分装,既包括了任务,也包括了任务的执行状态(NORMAL,COMPLETING,CANCELD,EXCEPTION等等)。
- WorkerRunnable(mWorker对应的类)是对详细任务的封装,是具体干活的。我们的doInBackground()就是在里面的call()中执行的。
AsyncTask的构造方法如下:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
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() {
//...
}
};
}
以上我们就得知了doInBackground的执行时机。然后我们可以在doInBackground中合适的地方调用publishProgress方法,该方法通过Handler/Message的方法去调用onProgressUpdate(),更新UI
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
4.任务完成
如果任务正常完成,将会在call()方法中调用postResult()方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
和更新进度一样,也是通过Handler的方法传递给主线程,执行finish()方法
private void finish(Result result) {
if (isCancelled()) {//由于取消而结束,最终调用onCancelded()回调
onCancelled(result);
} else {//由于正常结束,最终调用onPostExecute()
onPostExecute(result);
}
mStatus = Status.FINISHED;//更新状态,任务结束
}
5.任务取消或中断
执行AsyncTask的cancel()方法
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);//atomicBooleans 赋值 true
return mFuture.cancel(mayInterruptIfRunning);
}
停掉FutureTask线程,最终走finishCompletion()方法
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
U.compareAndSwapInt(this, STATE, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
/** * Removes and signals all waiting threads, invokes done(), and * nulls out callable. */
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {//停掉所有工作线程
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
//调用done()方法,该方法在AsyncTask构造方法中new FutureTask()对象时复写过
done();
callable = null; // to reduce footprint
}
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);
}
}
};
最终又回到了postResult()中,和正常结束走相同的流程:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
最终判断mCancelled标志位,决定调用onCanceled()方法!
从上面的代码可以看出,AsyncTask给我们提供了两个Runnable实际上是别有用意的,mFuture可以看成是对mWoker的封装,mWoker中只处理单纯的工作子线程的业务逻辑,而mFuture用来处理其他事情,例如线程入列,任务取消,停止等和Task整体相关的东西,非常棒的设计。
最后,参考了一篇简书上的文章