一、AsyncTask简介
AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类(轻量体现在使用方便、代码简洁),它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
AsyncTask的内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。
其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。
1.AsyncTask的泛型参数
public abstract class AsyncTask<Params, Progress, Result>
复制代码
- Params:开始异步任务执行时传入的参数类型;
- Progress:异步任务执行的过程中,返回下载进度值的类型;
- Result:异步任务执行结束后,返回的结果类型。
如果AsyncTask确定不需要传入具体的参数,那么可以用Void代替
2.AsyncTask的核心方法
onPreExecute()
,该方法在任务开始执行之前调用,在主线程调用,用于初始化界面上的初始化操作,例如显示一个进度框;doInBackground(Void... params)
,该方法在子线程中运行,我们所有的耗时操作都在这里处理。任务一旦结束可以通过return
将执行结果进行返回,如果第三个泛型参数是Void
,则不返回任务执行结果。注意,这个方法不可以进行UI操作,可以调用publishProgress(Progress... values)
来完成任务的执行进度;onProgressUpdate(Integer... values)
,当后台任务调用publishProgress(Progress... values)
方法后,该方法就会被调用,可以用返回的数据进行一些UI操作;onPostExecute(Boolean aBoolean)
,当doInBackground(Void... params)
执行完毕后并通过return
返回时,该方法很快就会被调用,返回的数据就会作为参数传递到该方法中,可以利用返回来的数据进行一些UI操作。
上面几个方法的调用顺序是:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
除了上面四个方法,
AsyncTask
还提供了onCancelled()
方法,它同样在主线程中执行,当异步任务取消时,onCancelled()
会被调用,这个时候onPostExecute()
则不会被调用,但是要注意的是,AsyncTask
中的cancel()
方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()
判断终止任务。就好比想要终止一个线程,调用interrupt()
方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。
3.AsyncTask的简单使用
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
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));
// Escape early if cancel() is called
if (isCancelled())
break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
//进行调用
new DownloadFilesTask().execute(url1, url2, url3);
复制代码
4.使用AsyncTask的注意事项
- 异步任务的实例必须在UI线程中创建,即
AsyncTask
对象必须在UI线程中创建。 execute(Params... params)
方法必须在UI线程中调用。- 不要手动调用
onPreExecute()
,doInBackground(Params... params)
,onProgressUpdate(Progress... values)
,onPostExecute(Result result)
这几个方法。 - 不能在
doInBackground(Params... params)
中更改UI组件的信息。 - 一个任务实例只能执行一次,如果执行第二次将会抛出异常。
二、AsyncTask源码分析
1.成员变量
//CPU数量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心线程数
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;
//线程没有任务执行时最多保持30s会终止
private static final int KEEP_ALIVE_SECONDS = 30;
//线程是缓存队列,默认容量为128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
//线程池工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
/内置线程计数器
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
//创建线程后计数器加1
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//串行线程池
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//并行线程池
public static final Executor THREAD_POOL_EXECUTOR;
//默认的线程池,采用的是串行的线程池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//任务执行完发送的消息标志
private static final int MESSAGE_POST_RESULT = 0x1;
//任务执行过程中更新任务进度的消息标志
private static final int MESSAGE_POST_PROGRESS = 0x2;
//内部持有的Handler,用来切换线程
private static InternalHandler sHandler;
//实现了Callable接口的worker,可以拿到返回值的Runnable
private final WorkerRunnable<Params, Result> mWorker;
//FutureTask,实现了RunnableFuture接口
private final FutureTask<Result> mFuture;
//当前任务的状态,采用volatile关键字修饰,保持内存可见性,默认为待执行状态
private volatile Status mStatus = Status.PENDING;
//任务是否取消的标志,用AtomicBoolean保持可见性
private final AtomicBoolean mCancelled = new AtomicBoolean();
//任务是否开始的标志,用AtomicBoolean保持可见性
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
复制代码
以上都是一些AsyncTask
类的成员变量,比较容易理解,需要注意的是三个线程池,一个是串行线程池,一个是并行线程池,还有一个是线程池的引用,串行线程池已经初始化,其他两个在静态代码块里初始化
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;
}
复制代码
2.构造方法
public AsyncTask() {
//创建一个Callable对象,将AsyncTask的实现类中的泛型Params,Result传递进去
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;
}
};
//创建一个FutureTask,将worker传递进去
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
,并把mWorker
作为参数传入mFuture
,那么这两个参数有什么作用呢?。
如果启动一个任务,就要调用该任务的execute()
方法,因此看一下该方法的源码:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
复制代码
接着又调用executeOnExecutor(sDefaultExecutor, params)
方法:
@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;
exec.execute(mFuture);
return this;
}
复制代码
很容易就可以看出,先执行了onPreExecute()
方法,然后执行耗时任务在exec.execute(mFuture)
,将已经实例化好的mFuture
传递进去了。
那么问题又来了,exec
是什么?
从参数传递上可以看出是sDefaultExecutor
,再追溯可以看到是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
。
THREAD_POOL_EXECUTOR
实际上是一个线程池,开启了一定数量的核心线程和工作线程,然后调用execute()
方法就开始执行异步任务了。
那么异步任务执行完了,又是怎么通知主线程更新UI?
我们先看前面构造方法中mWorker
的call()
方法的内容,先执行了doInBackground(mParams)
方法,再执行postResult(result)
。
private Result postResult(Result result) {
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(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;
}
}
}
复制代码
在InternalHandler 中,
- 如果收到的消息是
MESSAGE_POST_RESULT
,即执行完了doInBackground()
方法并传递结果,那么就调用finish()
方法。 - 如果任务已经取消了,回调
onCancelled()
方法,否则回调onPostExecute()
方法。 - 如果收到的消息是
MESSAGE_POST_PROGRESS
,回调onProgressUpdate()
方法,更新进度。
到此为止,AsyncTask
的源码分析完了。
三、AsyncTask出现的问题
AsyncTask
不与任何组件进行绑定,所以最好在Activity/Fragment
的onDestory()
方法中调用cancel(boolean)
方法;- 内存泄漏,如果
AsyncTask
被声明为Activity
的非静态的内部类,那么AsyncTask
会保留一个对创建了AsyncTask
的Activity
的引用。如果Activity
已经被销毁,AsyncTask
的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity
无法被回收,引起内存泄露。 - 屏幕旋转或
Activity
在后台被系统杀掉等情况会导致Activity
的重新创建,之前运行的AsyncTask
(非静态的内部类)会持有一个之前Activity
的引用,这个引用已经无效,这时调用onPostExecute()
再去更新界面将不再生效。