2019独角兽企业重金招聘Python工程师标准>>>
构造AsyncTask, 这里只需理解Worker, Future的概念。注意这里mWorker 和 mFuture 的关系, 以及 postResult 和done方法。
AsyncTask使用了模板方法的设计模式, doInBackground是其中的核心,被包装在mWorker对象中,mWorker对象又被包装在mFuture对象, mFuture本质是一个runnable
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask(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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
===============以上是背景。==================
从execute方法开始,内部调用了executeOnExecutor 方法,
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
这里的sDefaultExecutor 默认是SERIAL_EXECUTOR,是 SerialExecutor类型对象。 模板方法doInBackground的参数params就是在这里通过mWorker对象赋值的
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
...
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
AsyncTask中有两个重要的静态变量:SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/**
* 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();
接下来看 SerialExecutor 的execute 方法, 先把当前task放在队列mTasks尾部,然后取队列第一个task通过TREAD_POOL_EXECUTOR执行。 注意execute 和 scheduleNext 都是synchronized 方法, 在AysncTask中SerialExecutor的实例变量SERIAL_EXECUTOR又是一个静态成员,所以即使我们在不同的线程中new出来很多AyncTask对象分别提交不同的任务, 这些任务让仍然会被串行的加入任务队列。
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive;
public synchronized void execute(final Runnable r) {
// 任务包装后入队, 包装的额外功能就是使任务完成后自动调度下一个任务!
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//如果当前没有正在执行的任务就在队首调度一个新任务,
//否则等待当前任务完成,完成后会自动调度下一个任务, 这里的mActive是包装后的任务对象
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
// SERIAL_EXECUTOR调用了THREAD_POOL_EXECUTOR来具体执行任务
//注意这里的mActive是包装后的任务对象
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
千万注意,这里我们提交的任务是 r , 但是在放入mTasks队列时对 r 进行了如下的包装, 这里包装后的任务对象是匿名的,假设包装后的任务叫pr ( pseudo r 的意思) , 那么pr 的执行包括了两部分,首先执行具体的任务(r的run方法),然后不管任务执行是否成功(也就是不管r的run方法过程是否发生了异常),接着调度mTasks队列的下一个任务。所以当使用默认的AsyncTask时,如果有多处提交任务,那么这些任务将会按串行的方式执行,这就是为什么官方推荐只用AsyncTask执行短时任务, 任务耗时太长的话将会发生相互阻塞,这可能是你不想要的!
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
问题到这里已经清楚了。
=================end======================
接着说一下, TREAD_POOL_EXECUTOR 和 SERIAL_EXECUTOR都可以当做executor使用, 并且TREAD_POOL_EXECUTOR是可以并行的执行任务的(多线程),但是TREAD_POOL_EXECUTOR对AsyncTask是不可见的,SERIAL_EXECUTOR才是AsyncTask直接使用的executor对象。 TREAD_POOL_EXECUTOR只是作为SERIAL_EXECUTOR执行任务的工具,对SERIAL_EXECUTOR来说,它的作用就是可以提供线程来执行任务。所以如果你想改变AsyncTask不适合执行长时间任务的这个缺陷,需要定制自己的Executor来替换SERIAL_EXECUTOR。