1.AsyncTask基本使用
AsyncTask主要用来执行耗时操作,同时它把执行进度和结果传递给UI线程,因此很适合一些需要在执行完耗时操作后更新UI或者执行耗时操作过程在UI上显示进度的场景。
AsyncTask基本使用如下:
①首先自定义一个类继承AsyncTask
class MyAsyncTask extends AsyncTask<URL,Integer,Integer>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Integer doInBackground(URL... urls) {
int totalSize = 0;
for(int i=0;i<10;i++){
//执行一个下载任务
totalSize += Download.download(urls[i]);
publishProgress(i/10);
if(isCancelled)
break;
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... values) {
//更新显示下载进度的进度条
updateProgress(values[0]);
}
@Override
protected void onPostExecute(Integer results) {
ToastUtils.showShort("共下载"+results);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
②执行任务
new MyAsyncTask().execute(url1,url2);
AysncTask对象可以在主线程中创建,但是execute方法按照规范必须在主线程中执行。因为onPreExecute方法是在execute的线程中执行的。而构造函数内部的Handler则默认使用了主线程了Looper,所以AysncTask对象创建的线程不做要求。
主要有五个方法:
- onPreExecute:主线程中执行,可以用来做一些执行耗时操作之前的准备工作。
- doInBackground :在新线程中执行,必须实现的方法,用来做耗时操作。可以通过publishProgress方法调用onProgressUpdate显示下载进度。下载完成后会调用onPostExecute方法。
- onProgressUpdate:主线程中执行,调用publishProgress才会触发此方法。
- onPostExecute:主线程中执行,任务执行完成后会调用此方法。
- onCancelled:主线程中执行,任务取消时被调用。
AsyncTask接受三个参数,第一个参数传递给doInBackground作为执行耗时操作的参数,第二个参数传递给onProgressUpdate作为进度更新值,第三个参数传递给onPostExecute作为执行结果返回。AsyncTask的使用很简单,接下来就深入内部了解下它是怎么工作的吧。
2.AsyncTask源码分析
从哪里使用就从哪里开始,显然我们从new MyAsyncTask().execute(url1,url2);
中的execute方法作为入口,一探究竟。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute方法没做啥,就是调用了executeOnExecutor
。咦,有个不认识的家伙sDefaultExecutor
,这是什么呢?看下定义sDefaultExecutor的地方:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
Executor是个线程池框架接口,sDefaultExecutor指向SERIAL_EXECUTOR,那SERIAL_EXECUTOR内部应该是实现线程池类似的功能,来看看SERIAL_EXECUTOR定义的地方:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
找到了SERIAL_EXECUTOR的实现类SerialExecutor
了,来看看:
//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);//ThreadPoolExecutor真正执行任务
}
}
}
它靠一个ArrayDeque集合存储Runnable对象,execute方法就是往集合中加入Runnable。mActive是当前正在执行的Runnable的对象,如果mActive == null表示当前没有正在执行的任务,就会调用scheduleNext方法,从mTasks中取出Runnable对象,再放入线程池THREAD_POOL_EXECUTOR
中执行。THREAD_POOL_EXECUTOR的定义:
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
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;
}
ThreadPoolExecutor是线程池的真正实现,它的参数的含义这里就不多说了。所以可以看到AsyncTask内部有两个线程池,SerialExecutor和ThreadPoolExecutor,但是它们两个分工明确,SerialExecutor负责将任务一个一个存储起来,然后一次只取出一个任务放入到ThreadPoolExecutor中进行处理。SerialExecutor负责排队,ThreadPoolExecutor负责任务的真正执行。
回过头来看最初的executeOnExecutor
方法:
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;
}
用mStatus记录了当前Task的状态,mStatus初始值是Status.PENDING,一旦调用了execute之后就会赋值为Status.RUNNING,所以execute方法只能执行一次。
params我们知道是传入的参数,mWorker.mParams又是啥?先来看看mWorker的定义:
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
其中WorkRunnable
就是一个Callable对象,如下声明,FutureTask
可以异步执行Callable对象的call方法,并且返回结果。因此是先把参数传入到Callable中,而Callable又会作为mFuture的执行体,再把mFuture放入线程池exec中执行。exec即是最开始传入的sDefaultExecutor,即SerialExecutor串行线程池排队。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mWorker和mFuture都是在AsyncTask的构造方法中定义的,来看看:
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() {
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);
}
}
};
doInBackground方法在mWorker的call方法中被调用了,因此它是执行在子线程中的。任务结束后会执行FutureTask的done方法,done中调用了postResultIfNotInvoked
,我们来看看它里面做了什么:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
执行完任务后调用了postResult
方法,并且把result也传过去了,但是此时postResult还是执行在子线程中的,因此要用Handler把线程切换到主线程中,来看看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中发送了一条MESSAGE_POST_RESULT的Message,Message由getHandler
方法得到的Handler进行处理。找到这个Handler新建对象的地方,也在AsyncTask的构造方法中:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
……
}
通常构造方法中不会传入Looper,因此会走getMainHandler
方法,来看一下:
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
这里注意,InternalHandler用的是主线程的Looper,所以InternalHandler最终会在主线程中处理消息。
返回了一个InternalHandler对象,InternalHandler
继承了Handler类:
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;
}
}
}
可以看到之前发送的MESSAGE_POST_RESULT消息就会到这个InternalHandler中处理。然后调用了finish
方法,看看该方法做了什么:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
此时调用了onPostExecute,所以onPostExecute是在主线程中进行最后的结果处理的。
到这里AsyncTask的流程就走完了。AsyncTask内部就是两个线程池和一个Handler,SerialExecutor负责排队,ThreadPoolExecutor负责任务的真正执行,InternalHandler用于切换线程。
3.不同版本的AsyncTask的区别
Android1.6之前,AsyncTask是串行执行任务,
Android1.6之后并行处理,
Android3.0之后又变成了串行执行任务。Android3.0之后可以通过executeOnExecutor方法进行并行处理任务。
4.AsyncTask的缺陷
① AsyncTask并不会随着Activity或Fragment销毁而销毁。这就意味着AsyncTask的任务会一直执行直到任务结束。任务结束时,如果cancel方法调用了,则执行onCancelled方法,如果cancel方法没调用,则执行onPostExecute方法。
② 由于前面①中所说的AsyncTask在Activity销毁后还在继续执行任务,如果AsyncTask是非静态内部类,那么就会持有创建AsyncTask的Activity的引用,导致Activity无法回收,造成内存泄漏。
③ 当Activity由于屏幕旋转等原因导致重建时,AsyncTask会出现结果丢失的问题。Activity已经重建了,是一个新的对象,而AsyncTask中持有的引用还是之前的Activity,所以当AsyncTask执行完任务之后,onPostExecute没有任何作用。