在開發的過程中我們如果想進行一些耗時的操作不能直接在UI線程中進行,除了使用Handler機制來進行異步消息處理,Android還給我們提供了一個非常方便的類AsyncTask來進行異步操作,在這篇文章中只對AsyncTask的源碼進行一下梳理,如果對AsyncTask的使用還不太熟悉的可以先看一下API的使用方法。
我們往往是定義一個類繼承AsyncTask,然後創建出這個類的實例,而後調用execute方法來執行任務,我們就從這裏入手來分析源碼,首先是構造方法:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
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));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
首先看一下代碼的註釋,告訴我們這個構造器必須在UI線程中進行調用,這個在後面會給出分析。構造方法很簡單,分別對創建出了WorkerRunnable和FutureTask對象並將其保存起來。先看看WorkerRunnable是什麼東西:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
實現了Callable接口(可以簡單的將Callable對象看做一個執行run方法時有返回值的Runnable對象,只不過在Callable中改名叫call方法),是一個抽象類,在構造函數中重寫了call方法,暫時先不用考慮裏面的邏輯,後面會進行分析。
再看FutureTask類,它的構造方法中接受了一個Callable參數,也就是我們剛剛創建出來的WorkerRunnable對象,看一下它的方法列表不難找到一個比較熟悉的方法,run方法,我們進入看一下:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
只看一下關鍵的部分,在這個方法中調用了Callable對象的call方法。
構造函數已經簡單的分析完了,現在來看一下我們經常調用的execute方法:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
可以看到,它只是簡單的調用了另一個方法,但是出現了一個沒有見過的參數sDefaultExecutor,來看一下這個參數是什麼:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
* 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();
從註釋中可以看出這是一個一次只能執行一個任務的線程池,並且這是一個在進程範圍內公用的池,看一下它的定義:
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);
}
}
}
代碼不是很多,首先在線程池中維護了一個雙端隊列的實例,並且在線程池的execute方法中並沒有執行傳入的Runnable對象,而是將它用另一個新創建的Runnable對象包裝起來放到隊列的尾部。顯然,在第一次調用execute方法的時候mActive對象一定是null,這個時候會調用scheduleNext方法將隊列中的元素拿出來,並且使用THREAD_POOL_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);
只是創建了一個固定規格和模式的線程池,這裏不是分析的重點就不去深入它了。在執行完成後可以看到無論如何都會執行到SerialExecutor中execute方法的finally語句中,在這裏又調用了scheduleNext進行重複的工作。通過上面的分析也就不難理解註釋中說明的一次只能執行一個任務,雖然沒有繼續分析,但是可以猜想出AsyncTask的任務是由SerialExecutor進行分配,並且由於它是static的,在同一個進程內所有的AsyncTask中的任務都會放入到這個類的隊列中按順序執行。
接着回到AsyncTask的execute方法中,調用了executeOnExecutor方法,進入看一下:
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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在這裏可以看到熟悉的onPreExecute方法被調用了,由於前面的註釋中說過,AsyncTask類對象的創建必須是在主線程中執行的,所以onPreExecute方法的執行也是在主線程中的。而後將傳入的參數賦給了mWorker對象中的數組,使用傳入的線程池對mFuture進行執行,因爲傳入的參數是sDefaultExecutor,所以執行的邏輯跳轉回了SerialExecutor的execute方法,而後在線程池中執行了mFuture的run方法。根據我們前面的分析它的run方法調用了mWorker的call方法,那麼call方法是在哪裏實現的呢,沒錯,在構造函數中:
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
在這裏執行了doInBackground方法,這個方法應該很熟悉了,它是一個抽象的方法,也是每次都要求我們必須進行重寫的。這裏由於執行過程還是在線程池中進行的,所以避免了在UI線程中進行耗時操作。方法執行完成後將返回值作爲參數傳遞給了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機制,那麼這個Handler是在哪創建的呢,
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@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;
}
}
}
很顯然Handler類是靜態的,在類加載的時候就會初始化這個類,現在回過頭再來想一下剛纔提到的AsyncTask一定要在UI線程中加載的問題,如果不在UI線程中進行加載,那麼Handler自然也就不在UI線程中,那麼onProgressUpdate方法和onPostExecute方法中也就不能執行更新UI的操作了。
繼續看postResult方法,很簡單,就是給Handler發了一個消息並且傳了一個參數,而Handler調用了AsyncTask的finish方法用到了這個參數,下面是finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在任務被取消時調用onCancelled方法回調,不然就調用我們熟悉的onPostExecute方法。而在Handler的handleMessage中還有一個接收項,它回調了我們常用的onProgressUpdate方法,找一下這個標記(MESSAGE_POST_PROGRESS)是怎樣傳入的,不難找到publishProgress方法:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
這也正是我們在doInBackground中經常使用的。