让我们深入研究 SerialExecutor 类。在这堂课中,我们有final
ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
这实际上作为不同线程上不同请求的序列化程序。这是一个半同步半异步模式的例子。
现在让我们看看串行执行器是如何做到这一点的。请看一下 SerialExecutor 的代码部分,它写成
if (mActive == null) {
scheduleNext();
}
因此,当第一次在 Asynctask 上调用 execute 时,这段代码在主线程上执行(因为 mActive 将被初始化为 NULL),因此它会将我们带到 scheduleNext() 函数。ScheduleNext() 函数编写如下:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
因此,在 schedulenext() 函数中,我们使用已在 dequeue 末尾插入的 Runnable 对象初始化 mActive。这个 Runnable 对象(它只是 mActive)然后在从线程池中取出的线程上执行。在那个线程中,“finally”块被执行。
现在有两种情况。
已经创建了另一个 AsyncTask 实例,我们在执行第一个任务时调用它的 execute 方法。
在执行第一个任务时,第二次在 AsyncTask 的同一实例上调用 Execute 方法。
场景一:如果我们查看 的execute
函数SerialExecutor
,我们会发现我们实际上创建了一个新的可运行线程(Say thread t)来处理后台任务。在那个线程 t 中,我们运行mActive
. 但是由于它在 try 块中,finally 只会在该线程中的后台任务完成后才会执行。(记住 try 和 finally 都发生在 t 的上下文中)。在 finally 块中,当我们调用 scheduleNext 函数时,mActive
因为我们已经清空了队列,所以它变为 NULL。但是,如果创建了另一个相同的实例AsyncTask
并且我们在它们上调用了 execute,则这些的 execute 函数AsyncTask
将不会被执行,因为 execute 之前的同步关键字,也因为SERIAL_EXECUTOR
是一个静态实例(因此同一个类的所有对象都将共享同一个实例……它是类级锁定的一个例子)我的意思是同一个 AsyncTask 类的任何实例都不能抢占执行函数(因此后台任务在线程 t) 中运行。这一切意味着只有一个活动线程运行该任务。对于不同的任务,这个线程可能不一样,但一次只有一个线程会执行该任务。因此,只有在第一个任务完成时,后面的任务才会一个接一个地执行,这就是它被调用的原因SerialExecutor
。
场景二:在这种情况下,我们会得到一个异常错误。要了解为什么不能在同一个 Asynctask 对象上多次调用执行函数,请查看以下代码片段executeOnExecutor
,AsyncTask.java
特别是在下面提到的部分中:
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)");
}
}
从上面的代码片段可以清楚地看出,如果我们在任务处于运行状态时调用执行函数两次,它会抛出 IllegalStateException 说“无法执行任务:任务已经在运行。”。
你可以阅读我关于 AsyncTask 内部的讨论
https://docs.google.com/document/d/1_zihWXAwgTAdJc013-bOLUHPMrjeUBZnDuPkzMxEEj0/edit?usp=sharing