使用示例
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
// 此方法可在doInBackground中调用,发布进度更新
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
// 调用方法:
new DownloadFilesTask().execute(url1, url2, url3);
简述
AsyncTask中封装了两个线程池和一个handler,其中SerialExecutor用于任务的排队,而THREAD_POOL_EXECUTOR用于真正的执行任务
下面看一下该类中定义的属性
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
// 真正用于执行任务的线程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可见,该线程池核心线程数目为CPU数目+1,最大线程数量为2*CPU + 1
然后看一下另一个用于任务排队的线程池:
// 该线程池只是用于任务的排队,真正执行任务的还是THREAD_POOL_EXECUTOR
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);
}
}
}
该线程池只是一个串行的线程池,一个进程中所有的AsyncTask全都在这个串行的池中排队执行
首先,会将一个任务插入任务队列中,如果此时没有正在执行的任务,则调用scheduleNext执行一个任务,当任务执行结束,继续回调该方法,直道所有任务都执行结束
由此可见,其是串行执行的
下面从execute()方法开始看起:
new DownloadFilesTask().execute(url1, url2, url3);
注意:该方法必须在主线程中调用,默认实现是在线程池中串行执行的,如果需要并行执行的效果,可以调用executeOnExecutor方法,传入THREAD_POOL_EXECUTOR
execute又调用了executeOnExecutor(sDefaultExecutor, params);方法
该方法内部的实现逻辑为:
- 首先,检查当前的执行状态,若正在执行或者已执行,则会报错
- 调用onPreExecute()
- 调用传入的线程池去执行任务
下面再看sDefaultExecutor内部的实现
- 会将一个任务插入任务队列中,如果此时没有正在执行的任务,则调用scheduleNext执行一个任务,当任务执行结束,继续回调该方法,直道所有任务都执行结束
- 当执行futureTask的run方法时,会执行mWorker的call方法,因此,该方法最终会在线程池中执行
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
看一下call方法的逻辑:
调用doInBackground,之后,再将结果调用postResult方法发送出去
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
说到handler,也解释了AsyncTask 必须在主线程中加载的原因:
其内部的Handler是静态的,为了让其能成功切换执行环境,该Handler必须在主线程中创建,因此,该类必须在主线程中加载
消息发出之后,会回调finish方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
会根据是否取消,回调两种方法
总结
内部封装2线程池+handler,一个负责将任务串行排序,另一个负责真正执行任务;执行完毕后,通过Handler将结果,进度等回调出去。
自从3.0之后,默认是采用一个线程来串行执行任务,但仍可以通过调用executeOnExecutor方法,传入线程池来实现并行执行的效果
参考:《Android开发艺术探索》