讨论两个问题:
第一个问题:一个AsyncTask对象只能被执行一次,即只能调用一次execute;
第二个问题:既然一个AsyncTask对象只能被执行一次,为什么AsyncTask还要用线程池;
第一个问题
刚开始用AsyncTask的时候就知道一个AsyncTask对象只能被执行一次,即只能调用一次execute;
其实对这个概念是模糊的,一直再用也就没有关注,今天晚上特地研究了一下到底是什么意思,也就是代码怎么实现才算是一次,怎么实现算是多次;
下面用代码说明:
第一种情况:同时直接都execute
通过按钮点击触发上面的方法,控制台得到的结果是:
第二种情况:先让myAsyncTask执行完任务,然后我再执行execute;
通过按钮点击触发上面的方法,任务执行为三秒,控制台得到的结果是:
上面两个例子就很显然了,一个AsyncTask对象只能执行一次,关键点就在“一个对象”上;
佐证
如果到这还不明那就看下这个例子,同样是MyAsyncTask对象,但是我new多次,每new一次就代表着一个新的对象,看代码:
同样看下点击按钮后控制台的信息:
到这这里就很清楚了,一个AsyncTask对象只能执行一次。
注:我一直纠结的问题就是一个AsyncTask只能执行一次,我恰恰是忽略“对象”两个字(这也就是我一直单着的原因吧,哈哈哈)。
下面是MyAsyncTask,这个文章中的MyAsyncTask都是同一个:
第二个问题
在同一进程下的所有AsyncTask的任务都会到达同一个任务对队列:
我们从 new MyAsyncTask("onlyonce 1").execute(); 说起,现在看下源码
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
我们看到了executor调用的是executorOnExecutor在来看下它的源码
@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;
}
主要看exec.execute(mFuture)这一行。
exec是什么呢?从execute函数里面的实现就可以看到,exec是sDefaultExecutor,那么sDefaultExecutor是什么玩意呢?
从一下代码可以清楚的看到:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
sDefaultExecutor是SerialExecutor的一个实例,而且它是个静态变量。也就是说,一个进程里面所有AsyncTask对象都共享同一个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);
}
}
}
代码本身很简单,从execute里面就能看出,异步任务r被放到了ArrayDeque对象mTasks中,然后通过scheduleNext()来从mTasks里面得到一个任务去一个后台线程执行。