AsyncTask
初学 Android 的线程使用时接触最多的就是 AsyncTask 了,直到现在都还没有仔细查看过这个类的源码和实现,有愧于心啊。不说多的,本篇主要根据 AsyncTask 的类结构来进行简要说明,大致的介绍官方都写在文档或者注释中了,我们需要仔细研读。
AysncTask 只适用于最多几秒的操作场景,如果有更高的需求官方强烈推荐使用 Executor / ThreadPoolExecutor / FutureTask 等。AsyncTask分为几个部分:
postResult
当 doInBackground() 方法执行完毕后会将异步执行得到的结果回调给 postResult(),postResult() 将数据抛给InternalHandler。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
InternalHandler
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:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
我们看到 InternalHandler 的两种 case,前者调用了 finish() 传递数据到 onPostExecute() 方法,后者调用 onProgressUpdate() 方法更新进度条。AsyncTask 能够进行 UI 操作实际就是通过 InternalHandler 进行的
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在AsyncTask中内部获取 Handler 是由单例模式生成的实例:
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
AsyncTaskResult
充当 Entity 的作用。调用 onPostExecute 的时候是通过 handler 先将数据放在 AsyncTaskResult 对象中再传递到InternalHandler 中进行UI操作
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
ThreadPoolExecutor
AsyncTask 实际就是通过线程池实现的异步队列,可以不停的往队列中添加任务,边取边放以达到多线程的操作。
/**
* 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;
}
Status
枚举类,表示当前Task的状态
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
Binder
AsyncTask 构造函数开始就使用了Binder的方法:
/**
* Flush any Binder commands pending in the current thread to the kernel
* driver. This can be
* useful to call before performing an operation that may block for a long
* time, to ensure that any pending object references have been released
* in order to prevent the process from holding on to objects longer than
* it needs to.
*/
public static final native void flushPendingCommands();
WorkerRunnable
在构造函数初始化时有两个部分,一个是mWorker,另一个是mFuture。前者实现了Callable接口,目的是为的是接收外部传来的参数,通过实现Callable,以 call() 方法将用户参数传入 doInBackground() 得到 Result。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
Future
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);
}
}
};
executeOnExecutor
@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;
}
execute
想要启动AsyncTask,就必须执行此方法,方法中使用了sDefaultExecutor
/**
* Convenience version of {@link #execute(Object...)} for use with
* a simple Runnable object. See {@link #execute(Object[])} for more
* information on the order of execution.
*
* @see #execute(Object[])
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
*/
@MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
SerialExecutor
关键部分,AsyncTask 中的队列就是这个类的 ArrayDeque 实现的,最后 execute 执行的是这个类中的方法。
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
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) {
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);
}
}
}
AsyncTask 的使用注意事项
在官方的 性能优化典范(五) (中文戳这儿 —> ) Android性能优化典范 - 第5季中已经写得很明确了,以下引用自胡凯大神的译文:
4)Good AsyncTask Hunting
AsyncTask是一个让人既爱又恨的组件,它提供了一种简便的异步处理机制,但是它又同时引入了一些令人厌恶的麻烦。一旦对AsyncTask使用不当,很可能对程序的性能带来负面影响,同时还可能导致内存泄露。
举个例子,常遇到的一个典型的使用场景:用户切换到某个界面,触发了界面上的图片的加载操作,因为图片的加载相对来说耗时比较长,我们需要在子线程中处理图片的加载,当图片在子线程中处理完成之后,再把处理好的图片返回给主线程,交给UI更新到画面上。
AsyncTask的出现就是为了快速的实现上面的使用场景,AsyncTask把在主线程里面的准备工作放到onPreExecute()
方法里面进行执行,doInBackground()
方法执行在工作线程中,用来处理那些繁重的任务,一旦任务执行完毕,就会调用onPostExecute()
方法返回到主线程。
使用AsyncTask需要注意的问题有哪些呢?请关注以下几点:
- 首先,默认情况下,所有的AsyncTask任务都是被线性调度执行的,他们处在同一个任务队列当中,按顺序逐个执行。假设你按照顺序启动20个AsyncTask,一旦其中的某个AsyncTask执行时间过长,队列中的其他剩余AsyncTask都处于阻塞状态,必须等到该任务执行完毕之后才能够有机会执行下一个任务。情况如下图所示:
为了解决上面提到的线性队列等待的问题,我们可以使用AsyncTask.executeOnExecutor()
强制指定AsyncTask使用线程池并发调度任务。
- 其次,如何才能够真正的取消一个AsyncTask的执行呢?我们知道AsyncTaks有提供
cancel()
的方法,但是这个方法实际上做了什么事情呢?线程本身并不具备中止正在执行的代码的能力,为了能够让一个线程更早的被销毁,我们需要在doInBackground()
的代码中不断的添加程序是否被中止的判断逻辑,如下图所示:
一旦任务被成功中止,AsyncTask就不会继续调用onPostExecute()
,而是通过调用onCancelled()
的回调方法反馈任务执行取消的结果。我们可以根据任务回调到哪个方法(是onPostExecute还是onCancelled)来决定是对UI进行正常的更新还是把对应的任务所占用的内存进行销毁等。
- 最后,使用AsyncTask很容易导致内存泄漏,一旦把AsyncTask写成Activity的内部类的形式就很容易因为AsyncTask生命周期的不确定而导致Activity发生泄漏。
综上所述,AsyncTask虽然提供了一种简单便捷的异步机制,但是我们还是很有必要特别关注到他的缺点,避免出现因为使用错误而导致的严重系统性能问题。