Android AsyncTask

AsyncTask 是 Android 系统的一个轻量级异步类,它可以在线程池中执行后台任务,并且把执行的进度和结果传递给主线程。默认串行执行,已废弃。

优点:

  1. 使用简单;
  2. 子线程执行耗时操作,主线程更新进度和结果;

缺点:

  1. 系统兼容性问题;
  2. 不能真正取消任务,调用 cancel(true) 只是给任务做一下标记,等任务执行完成后回调 onCancelled() 方法;
  3. 一个 AsyncTask 实例只能执行一次任务;
  4. 内存泄漏问题;

AsyncTask 的泛型参数

1
2
3
4
5
6
/**
* 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.
*/
public abstract class AsyncTask<Params, Progress, Result> {}
  • Params: 开始异步任务执行时传入的参数;
  • Progress: 异步任务执行过程中,返回下载进度值的类型;
  • Result: 异步任务执行完成后,返回的结果类型;

如果 AsyncTask 确定不需要传递具体参数,那么这三个泛型参数可以用 Void 来代替。

核心方法

  1. onPreExecute()
    运行在主线程。会在任务开始执行之前调用,用于进行 UI 初始化操作,比如显示一个进度条对话框等。

  2. Result doInBackground(Params… params)
    运行在子线程。抽象方法,子类重写,用于执行耗时操作。

  3. onProgressUpdate(Progress… values)
    运行在主线程。任务在执行过程中,可以通过此方法将任务进度发布到 UI 线程,比如显示进度条。必须调用publishProgress(Progress... values)方法。

  4. onPostExecute(Result result)
    运行在主线程。任务执行成功后,返回的数据会作为参数传递到此方法中。比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

  5. onCancelled
    运行在主线程。任务执行失败后的回调方法。

线程池

AsyncTask 内置了两个线程池,分别是串行的 SERIAL_EXECUTOR 和并行的 THREAD_POOL_EXECUTOR。其中 SerialExecutor 线程池用于任务的排队,让需要执行的任务按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int KEEP_ALIVE_SECONDS = 3;

//静态,多个 AsyncTask 共享同一实例
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor THREAD_POOL_EXECUTOR;

static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

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 的作用是保存执行的任务和按 FIFO 的优先级串行调度任务,如果前面的任务比较耗时,后面的任务会响应不及时;
  2. 任务是在 THREAD_POOL_EXECUTOR 线程池中执行的,可以通过 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params) 执行并行任务;

拒绝执行策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static final int BACKUP_POOL_SIZE = 5;

private static final RejectedExecutionHandler sRunOnSerialPolicy =
new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// As a last ditch fallback, run it on an executor with an unbounded queue.
// Create this executor lazily, hopefully almost never.
synchronized (this) {
if (sBackupExecutor == null) {
sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
sBackupExecutor = new ThreadPoolExecutor(
BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
sBackupExecutor.allowCoreThreadTimeOut(true);
}
}
sBackupExecutor.execute(r);
}
};

THREAD_POOL_EXECUTOR 执行拒绝策略时,会创建新的线程池重新执行任务。

取消任务

AsyncTask 还提供了 onCancelled() 方法,它同样在主线程中执行。当异步任务调用 cancel() 取消任务时,onCancelled() 会被调用,而不是 onPostExecute() 方法。cancel() 方法并不是真正的取消任务,还是要等到任务执行完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}

private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}

我们需要在 doInBackground() 判断终止任务,和终止一个线程类似,调用 interrupt() 方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

使用 AsyncTask 的注意事项

  1. 版本兼容问题;
  2. execute(Params… params) 方法必须在 UI 线程中调用;
  3. 不要手动调用 AsyncTask 的几个回调方法;
  4. 一个 AsyncTask 任务只能执行一次,如果执行多次时任务的状态时 RUNNING 或 FINISHED,会抛出异常;
  5. 任务取消后并不会立即停止,而是等到任务完成后回调 onCancelled() 方法;
  6. AsyncTask 不应该与任何组件绑定生命周期,所以在执行 AsyncTask 时,最好在 Activity/Fragment 的 onDestory()调用 cancel(boolean),避免 crash 或内存泄露。

参考

[1] AsyncTask - Developer