讲在最前面的话,
AsyncTask
已经被官方废弃,本篇文章是从AsyncTask
源码角度来看一些值得学习的地方,怀揣一颗学习的心,我们可以从任何地方汲取知识的营养官方注释: "AsyncTask 旨在实现对 UI 线程的正确和简单使用。然而,最常见的用例是用于集成到 UI 中,这可能导致上下文泄漏、丢失回调或在配置更改时崩溃。它在平台的不同版本上行为不一致,会吞没来自 doInBackground 的异常,并且与直接使用 Executor 相比并没有提供太多实用性。
AsyncTask 被设计为围绕 Thread 和 Handler 的辅助类,并不构成通用的线程框架。建议将 AsyncTask 用于短期操作(最多几秒钟)。如果需要长时间运行的线程,强烈建议使用 java.util.concurrent 包提供的各种 API,如 Executor、ThreadPoolExecutor 和 FutureTask。
异步任务由在后台线程上运行的计算和在 UI 线程上发布结果组成。异步任务由 3 个泛型类型 Params、Progress 和 Result 定义,并包括 4 个步骤 onPreExecute、doInBackground、onProgressUpdate 和 onPostExecute。"
简介
AsyncTask
是 Android 平台提供的一个简便的类,用于在后台执行异步任务,而不会阻塞主线程,以保持用户界面的响应性。它可以用于处理一些耗时的操作,例如网络请求、数据库查询等。
有趣地说,AsyncTask
就像是一个勤劳的小助手,可以帮助你在后台完成一些繁重的工作,而你可以继续专注于其他的事情,比如喝杯咖啡或者玩手机游戏。
然而,AsyncTask
在 Android 的最新版本中已经被官方标记为过时(deprecated)。这是因为 AsyncTask
存在一些潜在的问题和限制,例如:
-
内存泄漏 :如果
AsyncTask
没有正确地取消或完成,它可能会导致内存泄漏,因为它持有对外部对象的引用。 -
配置更改问题 :当屏幕发生旋转或配置更改时,
AsyncTask
可能无法正确处理状态的保存和恢复,导致数据丢失或其他异常。 -
并发限制 :
AsyncTask
默认使用单个后台线程执行任务,这意味着同时只能执行一个任务。在某些场景下,可能需要并行执行多个任务,而AsyncTask
无法满足这种需求。
其实上述问题都是可以通过代码设计来解决的,但是这篇文章是为了介绍"过时"的AsyncTask
,因此不做过多介绍。常见的方法是及时在onDestroy
中释放引用,或者临时保存数据供后续使用。
但是过多的考虑异常case,就违背了选择AsyncTask
的初衷,因此Android 官方推荐使用其他的异步任务处理方式。
所以,尽管 AsyncTask
在过去是开发 Android 应用程序中常用的工具,但现在它已经被官方弃用,开发者应该优先考虑使用更现代的异步任务处理方式。
源码
线程通信的关键:Handler
在 AsyncTask
的内部,它维护了一个静态的 InternalHandler
类,该类扩展自 Handler
。这个 InternalHandler
用于在后台线程中发送消息给主线程。
下面是 AsyncTask
源码中 InternalHandler
的定义:
scala
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;
}
}
}
在 AsyncTask
中,当需要发送消息时,它会创建一个 Message
对象并将其发送到 InternalHandler
中。
在 AsyncTask
源码中有一个名为 postResult()
的方法,用于发送任务结果的消息。下面是 postResult()
方法的代码片段
sql
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
通过使用 InternalHandler
和 Message
,AsyncTask
实现了在后台线程和主线程之间进行消息传递的机制。后台线程可以通过调用 postResult()
或 postProgress()
方法来向主线程发送消息,而主线程中的 InternalHandler
会接收到这些消息并执行相应的操作,例如调用 onPostExecute()
或 onProgressUpdate()
方法来更新 UI。
AsyncTask
中的两个Executor
AsyncTask
存在两个Executor,一个是SERIAL_EXECUTOR
,这是一个串行执行的Executor,它可以确保所有任务都按照它们被提交的顺序执行。这对于需要避免多个任务同时访问共享资源的情况非常有用,因为它可以保证一个任务完成后再执行下一个任务。 具体的代码+注释如下:
js
java
// SerialExecutor 是一个实现了 Executor 接口的静态内部类。它用于按顺序执行多个任务,以避免多个任务同时运行导致的竞争和冲突。
private static class SerialExecutor implements Executor {
// 使用 ArrayDeque 存储待执行的任务队列。
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
// 当前正在执行的任务。
Runnable mActive;
// 实现 Executor 接口的 execute 方法,用于提交任务。
public synchronized void execute(final Runnable r) {
// 将任务包装成一个新的 Runnable,以便执行完后可以调用 scheduleNext() 方法继续执行下一个任务。
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线程池 来执行任务。
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
另一个是:THREAD_POOL_EXECUTOR
,官方解释翻译一下贴下来:
一个可以用于并行执行任务的 Executor。
已经过时了,对于不同的任务,使用单个线程池会导致子优化的行为。小型的-CPU-密集型任务可以从一个有界的池和队列中受益,而长时间阻塞的任务,例如网络操作,则可以从多个线程中受益。请使用或创建一个配置符合您使用情况的 Executor。
也就是说,之前设置了一个串行执行线程池,用来保障任务能够以有序的方式被调用,但是串行的阻塞性太强,因此再配一个并行的线程池来加快线程的工作效率。
ini
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
这里使用了SynchronousQueue
作为任务队列,可以通过生产者-消费者1对1的形式快速响应任务。