最近在review去年处理过的bug,发现一个关于AsyncTask的问题,反正最近也在review代码,那就好好学习一下Android开发中经常用到的线程相关知识吧。前面说的问题是这样的,相册在读取连拍照片时,会有一段时间的黑屏,但是照片的数量并不多,只有20张,使用Asynctask加载的话应该可以秒开才对,看了一下代码:
private AsyncTask mInitBurstDataTask = new AsyncTask() {
@Override
protected void onPreExecute() {
mDialog.show();
}
@Override
protected Void doInBackground(Void... voids) {
if(mBurstShotIdList != null) {
initBurstData(mBurstShotIdList);
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
showProgressBar();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
initAdapter();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.burstshot_main);
mInitBurstDataTask.execute();
}
上面的代码基本也是AsyncTask的通常用法,AsyncTask提供的四个核心方法,分别是:
1、onPreExecute()
工作在UI线程中,先于异步任务doInBackground()执行,例如界面的初始化工作;
2、doInBackground(Params… params)
工作在子线程中,处理耗时的任务,params是异步任务需要的参数,此时可以通过publishProgress(Progress… values)方法来更新任务的进度;
3、onProgressUpdate(Progress… values)
在主线程中执行,当在后台任务中调用了publishProgress(Progress… values)方法后,这个方法就会被调用,方法中携带的参数就是在后台任务中传递过来的;
4、onPostExecute(Result result)
当异步任务执行完之后在主线程中执行,result是异步任务结束后的返回值。
现在来看一下AsyncTask的构造函数:
/**
* 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
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
在AsyncTask构造函数中,初始化了两个变量,mWorker和mFuture,分别是Callable和FutureTask对象,也就是说我们new一个AsyncTask出来的时候,并没有开始执行什么具体的操作,我们知道,AsyncTask开始异步任务是从execute()开始的,来看一下这个方法的具体实现:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
仅仅是调用了executeOnExecutor(sDefaultExecutor, params)并出入了sDefaultExecutor和params两个参数,接着我们看一下executeOnExecutor()的源码:
@MainThread
public final AsyncTask 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;
}
从上面的代码中可以看到, onPreExecute()是最先执行的,然后把传进来的params参数赋给了mWorker,然后把mFutures传入sDefaultExecutor的execute()方法执行,至此,构造函数中初始化的两个变量都用到了。这时又转到了sDefaultExecutor上面来了,来看看它的定义:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
从上面的代码看出,sDefaultExecutor内部维护了一个双端队列mTasks,通过 mTasks.offer(Runnable)将传进来的mFuture(FutureTask实现了Runnable接口)插入队列中,从这里可以看出默认情况下AsyncTask的异步任务是串行执行的,我们从头梳理一下,传入的params赋值到mWorker,然后mWorker又被转入mFutureTask中去,然后将mFutureTask又被插入队列中,也就是说,params参数最终被封装称了FutureTask对象,而FutureTask是一个并发类,所以AsyncTask的异步任务是在FutureTask中得到执行的。上面代码的11行调用了mFutureTask的run()方法,这个方法中又会执行mWorker的call方法:
mWorker = new WorkerRunnable() {
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);
}
在mWorker的call()方法中,首先将mTaskInvoked设置为ture,表示当前任务已经被执行过了,接着执行AsyncTask中的doInBackground(mParams),然后通过postResult(result)把异步任务的结果发送出去,来看一下postResult()的源码:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
这里用到了sHandler来发送一个MESSAGE_POST_RESULT消息,接着看一下sHandler的具体实现:
private static InternalHandler sHandler;
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
sHandler收到了MESSAGE_POST_RESULT之后调用了AsyncTask的finish()方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在这里,首先判断AsyncTask是否被取消了,即是否调用了cancel(boolean mayInterruptIfRunning)这个方法,如果AsyncTask被取消了就调用onCancelled()否则调用onPostExecute(),可以看到doInbackground()异步任务的结果通过Handler传回到了主线程的onPostExecute()中,这样AsyncTask的一个异步任务流程就这样走完了。回头看一下InternalHandler中还有这样一个消息MESSAGE_POST_PROGRESS,不用解释,调用的正是onProgressUpdate()方法。
在前面说了,在默认情况下AsyncTask的异步任务是串行执行的,同一进程中同时开启了多个AsyncTask的话,如果前面的任务没有执行完成,后面的任务就会被阻塞,有没有办法可以让提交的任务马上执行呢?答案当然是肯定的,那就是调用AsyncTask的Task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)方法,THREAD_POOL_EXECUTOR是AsyncTask内部维护的静态线程池:
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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());
}
};
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
AsyncTask中有两个线程池,SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR,其中SERIAL_EXECUTOR是用于任务排队的,THREAD_POOL_EXECUTOR才是真正执行线程任务的。在我们调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)方法后,如果线程池中还有空闲的线程,我们的任务就会得到并行处理,此外我们还可以定义自己的线程池,这样就可以更好的控制线程的执行。
不要忘了,我们的bug还没有处理,在我们的代码中采用的是AsyncTask默认的任务执行方式(串行提交),现在采用并行的方式:
mInitBurstDataTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
重新编译apk,推入手机,果然可以到达秒开的预期效果。