AsyncTask源码分析
一 AsyncTask使用的场景
在MainThread线程中执行耗时不长的最多几秒钟的后台工作。
二 AsyncTask使用的方试
继承AsyncTasK,
重写
onPreExecute(),
doInBackground(),
onProgressUpdate(),
onPostExecute()
相应的方法,
至少需要重写doInBackground(),其它方法根据实际情况重写.源码中的注解已经直白的反映了是否可以UI刷新操作。
@MainThread
protected void onPreExecute() {
}
@WorkerThread
protected abstract Result doInBackground(Params... params);
@MainThread
protected void onProgressUpdate(Progress... values) {
}
@MainThread
protected void onPostExecute(Result result) {
}
三 . 回调方法的执行顺序(正常流程,刨除任务的重复提交和取消任务的场景)
- AsyncTask构造初始化后
- 调用execute(Params... params)方法后,
- 就先调用onPreExecute,
- 之后线程执行会调用doInBackground(),在这个方法执行你需要产生的耗时操作,
- 如果想更新耗时操作的进度, 则你可以在重写doInBackground()方法中调用publishProgress(Progress... values)方法, 此方法会让onProgressUpdate()方法执行。
- 线程用到的是FutureTask,最后在线程执行完后回调到done(),
- 在此后会触发onPostExecute();
四 .对每个方法运行在WorkerThread,MainThread说明
- onPreExecute 运行在MainThread, 反过来要求使用AsyncTask,在执行调用execute()时必需在UI thread中;
- doInBackground运行在WorkerThread,是通过FutureTask封装Callable,运行在新的线程中;
- onProgressUpdate,onPostExecute运行在MainThread, 是通过内部封装handle message机制WorkerThread切换到MainThread,即通过这样方式InternalHandler(Looper.getMainLooper())
五 AsyncTask泛型的说明
public abstract class AsyncTask<Params, Progress, Result>
- 第一个泛型参数Params 对应execute(Params... params)传入的参数,并在doInBackground中使用的参数。
- 第二个泛型参数Progress, publishProgress(Progress... values) onProgressUpdate(Progress... values)参数
- 第三个泛型参数Result,对应于是doInBackground方法的返回值类型和onPostExecute(Result result)
六 AsyncTask取消任务
1 . 需要对取消任务后做相应操作,可以重写
@MainThread
protected void onCancelled() {}
- 在需要取消的地方调研cancel(true)
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
- mCancelled.set(true)后
- 调用 iscancelled()并且返回true,
- 在doInBackground中返回后 ,执行postResult(result);
- 执行postResult(result)后,通过handle机制调用到finish(Result result)
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
通过finish的判断逻辑可知道不会执行onPostExecute(result),执行onCancelled(result);之后执行onCancelled();
通过取消流程, 如果有取消任务的场景,一般在实现doInBackground和onProgressUpdate做好判断.
七 AsyncTask使用规则
- The AsyncTask class must be loaded on the UI thread;
- The task instance must be created on the UI thread;
- execute() must be invoked on the UI thread;
- Do not call onPreExecute(), onPostExecute(),doInBackground(),onProgressUpdate();
- he task can be executed only once (an exception will be thrown if a second execution is attempted.)
八 线程池使用的变迁
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.
目前源码中使用的默认线程池
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);
}
}
}
1 SerialExecutor通过ArrayDeque队列来管理Runnable对象
-
第一调用execute时,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部, 然后判断mActive对象是不是等于null,
-
第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,
-
然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,
-
但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。
2 offer()方法里传入的Runnable匿名类
这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。