AsyncTask详解
实例代码:
private void doAsyncTask(){
AsyncTask<Integer, Integer, Integer> asyncTask = new AsyncTask<Integer, Integer, Integer>() {
@Override
protected Integer doInBackground(Integer... params) {
//在这里处理耗时事件 在后台的线程池中进行 异步的
return null;
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
//这里进行UI通知 在主线程中进行
}
};
asyncTask.execute(0);
}
AsyncTask说明
- 分析源码前,先说明,在 abstract class AsyncTask类中,常用元素均为static,所以说明了,但凡是使用AsyncTask方式进行异步处理,其提交的任务都由虚拟机系统统一管理每个进程的AsyncTask和处理。
- 在Android发展过程中,AsyncTask代码版本变化较大。早起引入时,AsyncTask实现方式是后台单一线程进行串行处理。从Android1.6开始,实现方式改变为使用了线程池允许多任务并行处理,但是在Android 3.0开始,任务默认任务执行为单线程,以避免多线程并行处理导致的程序错误。
- 如果你的确想使用并行处理,可以通过使用 executeOnExecutor(Executor exec, Params… params) 该函数进行多线程处理。
AsyncTask源码分析
execute(Params… params)
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
executeOnExecutor
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { ... onPreExecute(); // 这里就执行了onPreExecute 在主线程中进行 mWorker.mParams = params; //传参 exec.execute(mFuture); //执行 return this; }
这里需要分析exec对应的sDefaultExecutor:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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); } } }
SerialExecutor 是一个自定义的Executor,主要实现串行处理。 当executeOnExecutor->exec.execute(mFuture)执行的时候,对调用这里的execute方法,这个方法主要根据传入的Runnable–mFuture构造一个新的Runnable然后加入到mTasks队列的尾部。同时,判断当前系统是否有Task在执行,如果没有,调用scheduleNext 启动AsyncTask运行,处理mTasks队列中的Runnable…执行方式,通过ThreadPoolExecutor进行处理。这里要注意,虽然使用的是ThreadPoolExecutor,并且该线程池的容量不小,但是系统默认是使用的串行,单线程的。其控制原理在于上面所描述。每个Runnable执行完成后才会执行下一个 scheduleNext。
exec.execute(mFuture) 到底在做什么事情
从上面的分析可以看到程序走到SerialExecutor的scheduleNext逻辑上就走完了。但是,没有什么比较重要的,目前就执行了一个onPreExecute 在主线程中完成。经过分析,其重要的地方在于:
executeOnExecutor
mWorker.mParams = params; //传参
exec.execute(mFuture); //执行
execute传入了mFuture 已经对mParams进行赋值,后面的Runnable都是围绕着mFuture和mWorker进行,但是这两个成员是属于对象的,在一个对象的执行过程中很有可能被修改。所以,这个原因就会导致AsyncTask每个对象只能执行一次。为了保证这个,每个AsyncTask中保存了一个状态信息,系统通过mStatus来对状态进行控制。一旦mStatus == RUNNING 或者 FINISHED。。。就会抛出异常。并退出。
mFuture为何物
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
postResultIfNotInvoked(get());
...
}
};
其实mFuture就只是抛出了一个结果而已。应该也是隔回调,那么正在的调用在mWorker中,对于FutureTask的构造方法中会传入mWork
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception { //mWorker继承了Callable接口 添加了call方法。
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //调整该线程的优先级
//noinspection unchecked
Result result = doInBackground(mParams); //调用doInBackground
Binder.flushPendingCommands();
return postResult(result);
}
};
通过上面分析,可以知道 exec.execute传入了一个FutureTask, FutureTask构造的时候传入了一个Callback 并且实现了Call函数。当mTasks队列中的一个Runable被取出执行的时候会调用run方法,改方法会调用到传入的mFuture中的也就是FutureTask中的run方法:
public void run() {
...
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); //调用到了call方法
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); //如果异常 进程处理
}
if (ran)
set(result); //运行完进行结果处理
}
该run方法调用到了mWorker中的Call方法。至此,就实现了在一个线程中执行doInBackground方法。
关于setException和set方法 都是设置运行结果后调用了finishCompletion();
private void finishCompletion() {
...
done(); //调用了FutureTask的done方法,而这个方法刚好是之前mFuture中进行了定义
callable = null; // to reduce footprint
}
按之前所说 mFutureTask中的postResultIfNotInvoked进行了回调处理了结果。但是要注意,此时的调用环境还在后台线程中。所以,postResultIfNotInvoked函数中肯定使用了跨线程操作。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget(); //对结果进行了包装成Message然后通过handler进发送。
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper()); //这里使用了handler的另一种构造方法,所以重用了主进程的Looper...
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_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);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
同时在doInBackground我们可以通过publishProgress进行状态跟新,实现如下,也是利用的主线程的Looper构造的Handler发消息进行异步处理。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
如何使用多线程并行处理。
在google关于AsyncTask文档中说明了如果一定要使用多线程并行处理,那么应该使用executeOnExecutor传入一个线程池执行器。在上面的代码分析中,可以知道Executor是一个执行策略的定制,如下所述:
Executes the given command at some time in the future. The command
may execute in a new thread, in a pooled thread, or in the calling
thread, at the discretion of the {@code Executor} implementation.
所以,如果我们要使用多线程执行,那么就调用AsyncTask的execteOnExecutor函数,传入我们自定义的一个 Executor 代码如下:
private class MyExcutor implements Executor{
@Override
public void execute(Runnable command) {
AsyncTask.THREAD_POOL_EXECUTOR.execute(command);
}
}
MyExcutor excutor = new MyExcutor();
for (int i = 0; i < 10; i++){
new MyAsyncTask().executeOnExecutor(excutor, i);
}
如此,就可以实现使用AsyncTask使用线程池进行并行处理。
关于AsyncTask线程池
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
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 = //队列最长128 如果超过就无法执行
new LinkedBlockingQueue<Runnable>(128);
具体关于线程池的使用可查阅相关资料。