架构之异步任务
AsyncTask是啥?
AsyncTask是一个围绕Handler和Thread而设计的辅助类,封装了在工作线程中与UI交互的细节,只需要对应重写几个回调方法即可,并使得代码更加简洁,优雅。但要注意的是AsyncTask并不构成一个通用的线程框架 ,这在Android官方介绍中有提到:
AsyncTask is designed to be a helper class around {@link Thread} and
{@link Handler}* and does not constitute a generic threading framework.
AsyncTask的适用于短耗时操作,如果是长时间多线程执行,推荐使用java.util.concurrent包下的API,如Executor,ThreadPoolExecutor,FutureTask...
特性:
1.任务运行在后台线程,结果返回到UI线程
2.三个通用参数:Params,Progress,Result对应参数,进度和结果集
3.操作仅4步曲
onPreExecute
doInBackground
onProgressUpdate
onPostExecute
官方示例
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]);
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);
历史
September 2009: Android 1.6.之前,AsyncTask被执行在单线程上!
Android 1.6.以后:AsyncTask改变成,设计为线程池方式,允许多任务并发执行!
February 2011: Android 3.0.以后,AsyncTask的多任务只在单线程上被执行,为了避免不同版本因为并发执行带来的异常问题,但是仍然可以配置成多线程执行,只需调用executeOnExecutor(java.util.concurrent.Executor,Object[])方法,传入THREAD_POOL_EXECUTOR或者自定义Executor!
分析
1.最重要的2个成员
private final WorkerRunnable<Params, Result> mWorker;//核心工作人员
private final FutureTask<Result> mFuture;//对mWorker的一层包装,可以统计工作完成情况
2.构造方法,实例化工作人员
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);//后台任务开始执行
Binder.flushPendingCommands();
return postResult(result);//把结果交给Handler发出去,UI更新
}
};
mFuture = new FutureTask<Result>(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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
3.单线程模型(默认)
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);
}
}
}
默认的指挥官是SERIAL_EXECUTOR,字面意思是顺序执行器,内部维护着一个双端队列ArrayDeque:
ArrayDeque缺点是线程非安全,优点是效率高,用作堆栈时快于Stack,用作队列时快于LinkedList。
每接收一个任务,都会先把任务扔到队列里,然后检测当前是否有正在被执行的任务,没有的话,就从队列里取出head,交给另一个并发执行器去执行,如果当前有任务正在执行的话,就不做调度,默默的等任务执行完,如果执行完了,再调度下一个将要被执行的任务对象!
3.多线程模型(可选)
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;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
/**
* 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);
唯一的线程池指挥官ThreadPoolExecutor,权利大大的,拥有一个128容量的阻塞任务队列,集团化作战随时待命,空闲线程1s的存活时间,机动性能不是盖的:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
必杀技:
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
4.最平凡的工作人员:Callable
平常用的较多的创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。
这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
"造物主"为了解决这个问题,就提供了Callable和Future这对搭档,通过它们可以在任务执行完毕之后得到任务执行结果。
private static abstract class WorkerRunnable<Params, Result>
implements Callable<Result> {
Params[] mParams;
}
在本异步任务中当然不能缺席Callable,这里是用内部类去实现了,并且自带加特技Params,主要是方便把参数传递给doInBackground(mParams)~
4.最可敬的工作人员:FutureTask
FutureTask是RunnableFuture接口的实现类,RunnableFuture既包含Runnable特性,又兼具Future特点
上面说了Callable要配合Future使用,达到效果,而线程池执行者需要执行Runnable对象,所以用FutureTask最好不过了!
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
在异步任务构造器里分别完成了WorkerRunnable和FutureTask的实例化!
5.开始作战
第1步:下达命令
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
MainThread注解表示该方法只能在主线程调用~
第2步:装填弹药
@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();//UI前戏,预处理
mWorker.mParams = params;//绑定参数,传参
exec.execute(mFuture);//执行器执行任务
return this;
}
第3步:开火
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//线程优先级,后台线程
//noinspection unchecked
Result result = doInBackground(mParams);//回调到doInBackground,后台执行
Binder.flushPendingCommands();
return postResult(result);
}
第4步:打扫战场
protected void done() {
try {
postResultIfNotInvoked(get());//检测任务完成情况
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
第5步:向总部报告作战成果
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();//发消息
return result;
}
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT://报告结果-result
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS://报告进度
result.mTask.onProgressUpdate(result.mData);
break;
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);//回调UI,任务取消了
} else {
onPostExecute(result);//回调UI,任务执行完毕,结果是result
}
mStatus = Status.FINISHED;//更新状态
}
到此,异步作战就算完毕了~
此次作战学到了:
1.单兵作战神器SerialExecutor
2.群战(并发)神器,ThreadPoolExecutor
3.Callable与Future协同作战,威力大
4.群战(并发)法宝AtomicBoolean,原子操作
5.离不开的通信使者Handler
6.ArrayDeque双端队列,高效管理任务