雖然關於AsyncTask的文章有很多,並且文章對AsyncTask褒貶不一,本文關注的是AsyncTask的實現原理,以及它的優缺點,至於它的好壞,請讀者自行辨別。
什麼是AsyncTask
AsyncTask是android框架爲開發者提供的一個輔助類(只是一個類,不是庫)。
AsyncTask有什麼用
AsynTask的出現主要是爲了解決主線程和異步線程之間的交互問題的,說到這大家是不是想到了Handler,子線程,線程池等一些列的東西?如果沒有,那說明你還是太嫩啊!
其實AsyncTask就是Handler,子線程,線程池等技術的封裝,爲的是讓android開發者處理異步更方便,現在的開源庫也是這個道理。
AsyncTask怎麼使用
AsyncTask的使用非常簡單,讀者可以自行查找相關資料,如:官方文檔。
AsyncTask源碼分析
在執行異步任務的時候,我們首先會調用:
new mAsyncTask().execute();
因此,我們就從這裏下手!
首先讓我們來看看execute()
方法中的邏輯:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
是不是一看就驚了個呆,竟然只有一行代碼,有一行代碼不代表它簡單,接下來我們看看executeOnExecutor(sDefaultExecutor, params);
的邏輯。
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;
}
我們看到代碼中有一個if語句:
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代表當前的運行狀態,如果一個我們的asynctask正在運行,這時我們再一次執行了excute方法,就會引發throw new IllegalStateException("Cannot execute task:"
,而如果我們的asynctask已經運行結束了,這是我們再一次運行了excute方法,就會觸發
+ " the task is already running.");throw new IllegalStateException("Cannot execute task:"
異常,通過這段代碼,我們應該可以明白,爲什麼excute方法只能被執行一次。
+ " the task has already been executed "
+ "(a task can be executed only once)");
接下來代碼把狀態設置爲 mStatus = Status.RUNNING;
:這是因爲如果沒有異常,當然是正在執行啦。
接着我們就看到了我們非常熟悉的方法:onPreExecute();
(如果不熟悉,說明你還沒有掌握AsyncTask的應用,就先不要看源碼了,先學應用,一步一步來),這也說明了這個方法是首先被調用的,並且是在主線程中執行的。
接着往下看,出現了一行mWorker.mParams = params;
這樣的代碼,params我們可以知道,就是我們excute(Params…param)方法中傳過來的參數,可是mWorker
又是什麼呢?我們點進去看看:
private final WorkerRunnable<Params, Result> mWorker;
發現這是一個WorkerRunnable對象,那WorkerRunnable對象又是啥玩意,我們再點進去:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
額,原來實現了Callable接口,其中Callable接口中有一個call()方法。
我們再來看看mWorker是怎麼進行實例化的:
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);
}
};
可以發現call()方法返回了Result,那Result從哪裏來呢,唉 唉 唉 !!!,好像又發現了一個熟悉的方法:Result result = doInBackground(mParams);
,不過據說這個方法是在子線程中執行的,在這也沒見開子線程啊,這是怎麼回事?這個等會再說。
我們接着向下分析,執行到了exec.execute(mFuture);
這行代碼,此時出現了個mFuture,這是什麼東西呢?
private final FutureTask<Result> mFuture;
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);
}
}
};
它就相當於一個線程任務(Runnable);
exec是什麼東西呢?
是executeOnExecutor(sDefaultExecutor, params);
中傳過來的參數,sDefaultExecutor是啥呢?對,點進去看看:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
額,原來是一個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);
}
}
}
其實這就是一個線程池,線程池中的任務就是mFuture:當傳入一個任務後,execute方法會首先把該任務添加到mTasks
中,
if (mActive == null) {
scheduleNext();
}
這段代碼表示,如果當前沒有正在活動的任務,就調用scheduleNext()執行下一個AsyncTask任務,並且當一個任務執行完後,會繼續調用下一個任務進行執行,直到執行完所有的任務爲止,從這一點也可以看出AsyncTask是串行的。
其實在AsyncTask中有兩個線程池,一個是SerialExecutor
,另一個是THREAD_POOL_EXECUTOR
,前者用於任務的排隊,後者用於任務的執行。
還記得在上面我們遺留了一個問題,就是從哪裏可以看出來doInBackground(mParams)是在子線程中執行?我們再來分析一下這段代碼:
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);
}
};
在此看到這些代碼,是不是悟到了什麼?doInBackground(mParams)
在mWorker中,mWorker傳給了mFuture,mFuture又加入到了線程池中,接着返回了postResult(result)
方法,此方法我們也非常熟悉,就是把結果又返回到了主線程,我們看看這個方法具體是如何做的:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
這段代碼是不是面熟,對,就是Handler發送消息,它發送了一個MESSAGE_POST_RESULT
消息,接下來我們再看看它發送到的Handler是怎麼對消息進行處理的:
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;
}
}
}
我們可以發現,Handler是靜態的,並且Handler必須在主線程中進行創建,靜態成員是在類進行加載的時候進行初始化的,這也間接說明了AsyncTask必須在主線程中進行加載,handler收到MESSAGE_POST_RESULT
消息後,會執行result.mTask.finish(result.mData[0])
方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
此邏輯表示,如果我們取消了任務,就不再執行onPostExecute(result)
方法,否則,就把結果傳給onPostExecute(result)
.
OK,到此爲止,我們平時用的三個方法在源碼中都見到了,但是需要注意的是對於AsyncTask來說,google在不同的版本中改動是比較頻繁的,本文用的是API23,對於其他的版本請讀者按照同樣的分析方式自行分析即可。
當然,AsyncTask有很多的缺點,但是根據使用場景的不同,我們可以想辦法避免這些缺點,對於AsyncTask的一些缺點,請參考Android中糟糕的AsyncTask
如文章有錯誤,請留言告知