AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程。
一、简介
1.AsyncTask的泛型参数
public abstract class AsyncTask<Params, Progress, Result>
其中,三个泛型类型参数的含义如下:
- Params:开始异步任务执行时传入的参数类型;
- Progress:异步任务执行过程中,返回下载进度值的类型;
- Result:异步任务执行完成后,返回的结果类型;
如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。
2.AsyncTask的核心方法
onPreExecute()
这个方法会在后台任务开始执行之间调用,在主线程执行。用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
doInBackground(Params...)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。
任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,在主线程中进行,利用参数中的数值就可以对界面元素进行相应的更新。
onPostExecute(Result)
当doInBackground(Params...)执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,在主线程中进行,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
onCancelled()
在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
3.简单使用
class DownloadTask extends AsyncTask<Integer, Integer, String>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(Integer... params) {
for(int i=0;i<=100;i++){
publishProgress(i);
try {
Thread.sleep(params[0]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "执行完毕";
}
@Override
protected void onProgressUpdate(Integer... progress) {
//这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数
//但是这里取到的是一个数组,所以要用progesss[0]来取值
//第n个参数就用progress[n]来取值
tv.setText(progress[0]+"%");
super.onProgressUpdate(progress);
}
@Override
protected void onPostExecute(String result) {
//doInBackground返回时触发,换句话说,就是doInBackground执行完后触发
//这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"
setTitle(result);
super.onPostExecute(result);
}
}
需要在主线程启动
new DownloadTask(1).execute();
二、源码分析
构造函数
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);
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);
}
}
};
}
初始化了两个变量,mWorker和mFuture,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象。 FutureTask实现了Runnable接口。
mWorker中的call()方法执行了耗时操作,即result = doInBackground(mParams);,然后把执行得到的结果通过postResult(result);,传递给内部的Handler跳转到主线程中。在这里这是实例化了两个变量,并没有开启执行任务。
execute()方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
可以 看出,先执行了onPreExecute()方法,然后具体执行耗时任务是在exec.execute(mFuture),把构造函数中实例化的mFuture传递进去了。该方法执行过后会把任务的执行状态赋值为RUNNING,如果执行任务状态为RUNNING的话就会报错,也就是说一个实例不能执行两次。
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);
}
}
}
SerialExecutor 内部维持了一个队列,通过锁使得该队列保证AsyncTask中的任务是串行执行的,即多个任务需要一个个添加到该队列中,然后执行完队列头部的再执行下一个,以此类推。因为SerialExecutor 是个静态内部类,是所有实例化的AsyncTask对象公有的,所以说多个地方提交异步任务执行其实不会并行执行而是串行执行。
在这个方法中,有两个主要步骤。
- 向队列中加入一个新的任务,即之前实例化后的mFuture对象。
- 调用 scheduleNext()方法,调用THREAD_POOL_EXECUTOR执行队列头部的任务。
由此可见SerialExecutor 类仅仅为了保持任务执行是串行的,实际执行交给了THREAD_POOL_EXECUTOR。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
看名字也知道这是一个线程池,开启了一定数量的核心线程和工作线程,并允许核心线程超时回收
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发送
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
AsyncTask中实例化的Handler是静态内部类实现的,无论实例化多少AsyncTask对象用的都是一个Handler。并且为了可以把结果返回到主线程,要求AsyncTask必须在主线程实例化。
在handlerMessage的处理中将消息分为两类,一类是结果,走finish()方法,一类是进度,回调onProgressUpdate()
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在finish()中又分成了两类,一类是任务取消,回调onCancelled(),一类是任务完成,回调onPostExecute()
三、注意事项
1.生命周期
AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
2.内存泄漏
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
3. 结果丢失
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
4.AsyncTask并行执行
从Android3.0开始,我们可以通过直接调用AsyncTask方法的executeOnExecutor方法传入自己定义的线程池,你也可以传入AsyncTask内部已经定义的THREAD_POOL_EXECUTOR线程池。
四、总结
使用 AsyncTask是创建AsyncTask对象之后执行execute,创建AsyncTask对象的时候会同时创建一个WorkerRunnable 对象,并且以这个WorkerRunnable对象为参数会创建一个FutureTask对象,那么分析AsyncTask的原理就该从execute方 法开始了,执行execute方法首先会执行executeOnExecutor方法,并且传入一个SerialExecutor类型的对 象,SerialExecutor是一个串行线程池,一个线程里面的所有AsyncTask全部都在这个串行的线程池中排队执行,在 executeOnExecutor里面首先会执行onPreExecute方法,该方法是在我们创建AsyncTask对象的时候自己实现的,运行在主 线程中,我们可以在这个方法里面进行任务开始的提示性操作,接着线程池开始执行,也就是从这一步开始切换到了子线程中,传入的对象就是我们创建 AsyncTask对象的时候生成的FutureTask对象,在SerialExecutor线程池的execute方法中首先会把当前 FutureTask对象插入到任务队列中,如果当前任务队列中没有正在活动的AsyncTask任务的话,则会执行scheduleNext方法从队列 中取得一个AsyncTask任务,同时当一个AsyncTask任务执行结束之后会在finally中调用scheduleNext方法执行任务队列中 的下一个AsyncTask任务,从这里也看出来默认情况下AsyncTask是串行执行的,真正的执行操作就该在scheduleNext方法里面 了,这个方法里面真正执行任务的线程池是THREAD_POOL_EXECUTOR,SerialExecutor线程池是是用来任务排队的,保证默认情况下的串行执行,而THREAD_POOL_EXECUTOR才是真正的任务执行者,此外在 AsyncTask里面还有一个InternalHandler对象,其实他就是一个Handler对象而已,他存在的作用就是为了从子线程切换到主线程 中,为了便于在子线程执行的过程中进行一些与界面元素的交互过程,比如下载进度条的更新等等,那么也就必须要求该InternalHandler对象在主线程中创建了,查看源码发现InternalHandler对象是static的,也就是在AsyncTask对象创建的时候他就会创建,因此只要保证AsyncTask对象在主线程中创建就可以了,因此使用AsyncTask的时候一定要注意在主线程中创建他的对象,THREAD_POOL_EXECUTOR会执行他的execute方法,该方法实际上执行的是FutureTask的run方法,而 FutureTask的run方法实际上执行的是创建FutureTask对象的时候传入的参数WorkerRunnable对象的call方法,查看 call方法可以看到执行了doInBackground方法,该方法也是需要我们在创建AsyncTask对象的时候自己实现的,我们可以在这个方法里 面执行一些比较耗时的操作,它运行在子线程中,在该方法中我们可以通过publishProgress来发送一些耗时任务已经处理的进度信息,该方法运行在子线程中,该方法中会通过InternalHandler将进度消息发送出去,接着在InternalHandler里面的handleMessage 里面会发现是通过onProgressUpdate进行消息处理的,该方法运行在主线程中,可以进行更新进度条的一些操作,在 doInBackground方法执行结束后会将返回结果作为参数传递给postResult方法,该方法同样会通过InternalHandler发送 消息,最后在InternalHandler里面的handleMessage里面处理该消息,调用的是finish方法,也就是将线程切换到了主线程中 了,在finish方法中会根据主线程有没有被暂停来执行onCancelled或者onPostExecute方法,这两个方法是运行在主线程的,到这 里AsyncTask的执行结束了;