异步任务AsyncTask小结
那些优秀的文章,往往长时间的打磨诞生的结果,没有人天生就是好文笔。看有些人写一个话题,一周看代码,四周写文章,足以看出文章是要下功夫的。和别人比起来,我基本上是草稿的状态就发布了。写文章的目的不一样吧,我写文章是为了加强记忆,然后把我想说的记录下来(往往是让我醍醐灌顶那一下,或者说是觉得有意思的地方)。但从读者的角度出发,一篇好文章往往是让把一个复杂的原理通过最简单的逻辑关系串联起来,让小白也能听懂😁 。 且不管那么多了,记我想记,写我所思吧。
本篇开始之前,强烈建议先看一看 一篇讲明白FeatureTask
因为 AsyncTask
是基于 FeatureTask
实现的,明白了 FeatureTask
的使用才能更好理解 AsyncTask
先看一个小段代码:
class MyTask(private val taskName: String) : AsyncTask<String, Int, String>() {
// main thread
override fun onPreExecute() {
Log.e(FourthActivity::class.java.name, "$taskName : ${Thread.currentThread().name} - onPreExecute")
}
// subThread
override fun doInBackground(vararg params: String?): String {
Log.e(FourthActivity::class.java.name, "$taskName : ${Thread.currentThread().name} - doInBackground")
TimeUnit.SECONDS.sleep(2)
return ""
}
// main thread
override fun onCancelled() {
Log.e(FourthActivity::class.java.name, "$taskName : ${Thread.currentThread().name} - onCancelled")
}
}
// 按钮点击
click.setOnClickListener {
val t1 = MyTask("T1")
val t2 = MyTask("T2")
val t3 = MyTask("T3")
val t4 = MyTask("T4")
t1.execute()
t2.execute()
t3.execute()
t4.execute()
}
实际的打印结果:
2020-09-18 20:57:04.411 : T1 : main - onPreExecute
2020-09-18 20:57:04.412 : T2 : main - onPreExecute
2020-09-18 20:57:04.412 : T3 : main - onPreExecute
2020-09-18 20:57:04.412 : T4 : main - onPreExecute
2020-09-18 20:57:04.412 : T1 : AsyncTask #2 - doInBackground
2020-09-18 20:57:06.455 : T1 : main - onPostExecute
2020-09-18 20:57:06.459 : T2 : AsyncTask #3 - doInBackground
2020-09-18 20:57:08.502 : T2 : main - onPostExecute
2020-09-18 20:57:08.504 : T3 : AsyncTask #3 - doInBackground
2020-09-18 20:57:10.547 : T3 : main - onPostExecute
2020-09-18 20:57:10.548 : T4 : AsyncTask #3 - doInBackground
2020-09-18 20:57:12.591 : T4 : main - onPostExecute
结论: 多个 AsyncTask
任务同时执行 execute时,会在进行排队,等第一个任务执行结束之后,才会开启下一个任务执行。(注意时间间隔)。即便是不同的 AsyncTask
实现类,也会走同一个排队通道😁。
AsyncTask
中任务排队是由 private static volatile Executor sDefaultExecutor
统一进行管理的,我们来看一下它的实现:SerialExecutor
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;
}
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); // 线程池
}
}
}
看到了吧,只要有Runnable
任务进到队列里来就会存起来,取一个任务出来,交给线程池去处理,直到这个Runnable
的方法执行完成之后,才会去取下一个塞进来的 Runnable对象
,因为是 sDefaultExecutor
是静态的,所以只维护了一份排队的任务队列,因此才会出现上边的那种现象。
AsyncTask
的代码不多,我就不在这里贴代码了,如果会用 FeatureTask
,AsyncTask
代码应该很简单。
这里用简单几句话描述一下 AsyncTask
的实现过程。
当执行 AsyncTask#execute
时,会构建一个FeatureTask
对象,放入到 AsyncTask
公共的缓存队列中,缓存队列会依次取出(串行)放入的 FeatureTask
对象 交给线程池去执行(注意:是一个任务执行完成后才会取下一个任务),异步任务处理完成(无论是正常返回、用户取消)后,会通过Handler 发送消息给到主进程,然后调用对应的 onPostExecute
或 onCancel
等方法,这样一次异步任务执行完成。
AsyncTask
中有两个 Executor
,不知道大家能不能分清它们的不同,或者说 Executor
和 线程池是什么关系。
public interface Executor {
void execute(Runnable command);
}
Executor
接口 只定义了一个方法: execute
,它接收 Runnable
类型的参数。
Executor可以执行提交的Runnable对象。这个接口提供了一种将任务提交和任务执行解构的方式。通常情况下使用 Executor 而不是显示创建线程,例如,通常情况下不是为每一个任务调用: new Thread(new Runnable()).start()。 你可以使用:
Executor executor = anExecutor();
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
然而,Executor并没有规定说要求任务的执行时异步的,具体的需要看具体实现类。最简单的情况下,Executor可以立即在调用者的线程中运行提交的任务:
class DirectExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}}
更典型的情况是,任务会在其他线程执行,而非调用者线程。下面是为了每一个任务创建一个新线程的实现
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}}
还有一些Executor 还提供了一些对任务进行排序以及对提交的任务进行管理的实现。接下来是一个实现: 将提交给Executor的任务按顺序提交给另一个 Executor。 类似AsyncTask
中的 Executor的实现方式
class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<>();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
tasks.add(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}}
ExecutorService
扩展了 Executor
,ThreadPoolExecutor
线程池是 它的一种实现。
public interface ExecutorService extends Executor {
void shutdown();
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
...
}
到这里应该解释清楚了 Executor 和 ThreadPoolExecutor 的关系。也应该清楚了 AsyncTask
中两个 Executor
的作用。