AsyncTask
AsyncTask 是一个轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进入和最终结果传递给主线程并在主线程中更新UI。它封装了Thread 和Handler,但是AsyncTask 不适合执行特别耗时的后台任务,对于特别耗时的任务建议采取使用线程池。
AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下,如果不需要某个参数,可以设置为 Void
1. Params(参数类型)
在执行AsyncTask时需要传入的参数,可用于在后台任务中使用,是excute()任务执行方法和 doInBackground(Params...)的输入参数,通常为String
2. Progress(后台任务执行的进度类型)
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位,是 onProgressUpdate(Progress...)和 publishProgress(Progress...)方法的参数,是一个数组类型的参数,一般为Integer
3. Result(返回的结果类型)
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型,是onPostExecute(Result)方法的输入参数
如果AsyncTask 确实不需要传递具体的参数,这三个泛型参数可以使用Void 替代。
AsyncTask 的声明如下所示:
public abstract class AsyncTask<Params, Progress, Result> {
因此,一个最简单的自定义AsyncTask就可以写成如下方式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
……
}
这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。
一个异步任务的执行一般包括以下几个步骤。
1.execute(Parmas...parmas)
执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行
2. onPreExecute()
执行在 UI 线程,在execute(Parmas...parmas)被调用后立即执行,这个方法会在后台任务开始执行之前调用,调用后立即执行,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
3. doInBackground(Params...)
在线程池中执行,在onPreExecute() 完成后立即执行,这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。此方法将接收输入参数和返回计算结果,并将计算结果给onPostExecute 方法。在执行过程中可以调用 publishProgress(Progress...) 方法来更新任务进度信息。publishProgress 会调用 onProgressUpdate 方法。
4. onProgressUpdate(Progress...)
执行在 UI 线程,当在后台任务中调用了 publishProgress(Progress...) 方法后,这个方法就会很快被调用,直接将进度信息更新到 UI 组件上。
5. onPostExecute(Result)
执行在 UI 线程,当后台操作结束时,该方法被调用,doInBackground(Params...) 函数返回的计算结果将作为参数传递到此方法中,直接将结果显示到 UI 组件上。比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
上面这几个方法,onPreExecute 先执行,接着是doInBackground,最后才是onPostExecute。除了上述四个方法以外,AsyncTask 还提供了 onCancelled 方法,它同样执行在UI 线程,当异步任务被取消时,onCancelled 方法会被调用,此时onPostExecute 方法则不会被调用。如下所示:
public class DownloadFilesTask
extends AsyncTask<URL, Integer, Long>
{
//执行在线程池中
@Override
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);//更新下载任务进度
if (isCancelled()) {//判断下载任务是否取消
break;
}
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
@Override
protected void onPostExecute(Long result) {
showDialog("Downloaded" + result + "bttes");
}
}
注意doInBackground 和 onProgressUpdate 方法他们的参数中均包含 ... 的字样,在Java 中 ... 表示参数的数量不定,它是一种数组类型的参数。当要执行上述下载任务时,可以通过如下方式来完成:
new DownloadFilesTask().execute(url1,url2.url3);
在使用的时候,有以下几点需要格外注意(AsyncTask 的局限):
1、AsyncTask 对象必须在 UI 线程中创建,因为在AsyncTask 中会构建一个 Handler
2、execute(Parmas...parmas)方法必须在 UI 线程中调用,因为在AsyncTask 中会构建一个 Handler
3、不能在 doInBackground(Params...) 中更改 UI 组件信息,因为在子线程中执行
4、不要在程序中直接调用 onPreExecute()、onPostExecute()、doInBackground() 和 onProgressUpdate() 方法
5、一个任务实例只能执行一次,如果执行第二次将会抛出异常
6、在Android1.6 之前,AsyncTask 是串行执行任务的,在Android 1.6 AsyncTask 采用线程池里处理并行任务,但从Android 3.0 开始,为避免AsyncTask 所带来的并发错误,AsyncTask 又采用一个线程来串行执行任务。在Android 3.0 之后的版本中,我们仍然可以通过AsyncTask 的 executeOnExecutor 方法来并行的执行任务。
AsyncTask 的工作原理:
分析AsyncTask 的工作原理,从它的excute 方法开始,如下所示:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute 方法会调用 executeOnExecutor 方法,源码如下所示:
private final WorkerRunnable<Params, Result> mWorker;
@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;//将参数赋值给mWorker
exec.execute(mFuture);//mFuture 会交给 SerialExecutor 的 execute 方法处理
return this;
}
在上述代码中,sDefaultExecutor 实际上是一个串行的线程池,一个进程中所有的AsyncTask 全部在这个串行的线程池中排队执行,在 executeOnExecutor 方法中,AsyncTask 的onPreExecute 方法最先执行,然后线程池开始执行,其执行过程如下所示:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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) {
//将FutureTask 对象插入到任务队列 mTasks 中,
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//如果当前没有正在活动的AsyncTask,则调用scheduleNext
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {//执行下一个AsyncTask 任务
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
从上面代码可知,首先系统会把AsyncTask 的Params 参数封装为 FutureTask 对象,FutureTask 是一个并发类,在这里充当了Runnable 的作用。接着这个 FutureTask 会交给 SerialExecutor 的 execute 方法处理,SerialExecutor 的 execute 方法首先会把FutureTask 对象插入到任务队列 mTasks 中,如果这个时候没有正在活动的AsyncTask,则调用scheduleNext方法来执行下一个AsyncTask 任务。 同时当一个AsyncTask 任务执行完后,AsyncTask 会继续执行其他任务知道所有的任务都被执行为止,从这点可以看出,默认情况下,AsyncTask 是串行执行的。
AsyncTask 中有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR )和一个 Handler(InternalHandler),其中线程池SerialExecutor 用于任务的排队,线程池 THREAD_POOL_EXECUTOR 用于正真的执行任务,InternalHandler 用于将执行环境荣线程池切换到主线程。THREAD_POOL_EXECUTOR 的定义如下所示:
public static final Executor THREAD_POOL_EXECUTOR;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU核心数
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;//超时时间
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);//任务队列容量
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;
}
AsyncTask 对 THREAD_POOL_EXECUTOR 的配置规格如上所示。
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);//调用doInBackground 并获取返回值
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);//调用postResult 方法,并将返回值作为参数传递过去
}
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);
}
}
};
}
由于FutureTask 的run 方法会调用 mWorke 的call 方法,因此 mWorke 的call 方法最终会在线程池中执行。FutureTask 的run 方法如下所示:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在mWorke 的call 方法中,首先将 mTaskInvoked 设未 true,表明当前任务已经被调用过了,然后执行AsyncTask的 doInBackground 方法,并将返回值传递给 postResult 方法,postResult 方法如下所示:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult 方法会通过 mHandler 发送一个 MESSAGE_POST_RESULT的消息,mHandler 的定义如下所示:
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;
}
}
}
mHandler 是一个静态的Handler 对象,为了能够将执行环境切换到主线程,这就要求 mHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相的要求 AsyncTask 的类必须在主线程中加载,否则同一个进程中的 AsyncTask 都将无法正常工作。
mHandler 接收到 MESSAGE_POST_RESULT 消息后会调用 AsyncTask 的finish 方法,如下所示:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
从上述代码可知,如果AsyncTask 被取消了,就调用 onCancelled 方法,否则就会调用 onPostExecute 方法,而onPostExecute 方法的参数就是 doInBackground 方法的返回值,以上就是AsyncTask 的整个工作过程。
总结:
AsyncTask 构建的时候会做以下几件事情:
1、检查当前任务是否已经执行,如果已经执行,则抛出异常,如果没执行,则将当前任务的状态置为执行状态,接着调用onPreExecute() 方法,用于执行后台任务前的初始化操作。
2、构建一个IntentHandler 用于切换线程,构建两个线程池,一个用于执行任务,一个用于给任务排队,同时还会构建一个workRunnable 并将执行的参数封装成一个FuterTask 任务交给线程池排队和执行,在workRunnable 中,会调用 doInBackground 执行后台任务,并将执行结果通过postResult 方法发送给Handler,用于更新任务执行的进度及最终的执行结果。