前言:AsyncTask 是 Android SDK 中提供的一個用于執(zhí)行異步任務(wù)的框架,在 Android 興起的早期被廣泛使用,但如今在 API 30 被棄用,取而代之的是 java.util.concurrent 或 Kotlin 協(xié)程。雖然它存在著一些不足,但是我們還是可以嘗試了解一下它的實現(xiàn)原理以及存在的不足。
注:本文以 API 29 的 AsyncTask 源碼進行解析。
功能概述
首先,讓我們來簡單地了解一下它的設(shè)計初衷:
AsyncTask的設(shè)計初衷是能夠幫助用戶方便地完成異步任務(wù)的線程調(diào)度,它對用戶提供了如下幾個接口:
public abstract class AsyncTask<Params, Progress, Result> {
@WorkerThread
protected abstract Result doInBackground(Params... params);
@MainThread
protected void onPreExecute() {
}
@MainThread
protected void onPostExecute(Result result) {
}
@MainThread
protected void onProgressUpdate(Progress... values) {
}
@MainThread
protected void onCancelled(Result result) {
onCancelled();
}
}
首先看到它的三個范型參數(shù):
- Params:代表傳遞給它的參數(shù)類型
- Progress:代表用于反饋進度的數(shù)據(jù)類型
- Result:代表異步執(zhí)行的結(jié)果類型
用戶可以通過實現(xiàn)doInBackground方法來編寫在異步線程所要進行的處理;通過onPostExecute方法的重寫實現(xiàn)對異步請求結(jié)果的獲?。煌ㄟ^onPreExecute實現(xiàn)了在doInBackground之前進行一些預(yù)處理;通過onProgressUpdate實現(xiàn)對進度的監(jiān)聽;通過onCancelled實現(xiàn)對任務(wù)中斷的監(jiān)聽。
當我們編寫一個AsyncTask后,只需要調(diào)用它的execute方法即可,而背后的線程調(diào)度的過程都會由它替我們完成,看上去是十分美好的。
同時從上面的代碼中可以看到,每個方法都有被@MainThread的注解標注,這些注解主要的作用是用來標注這個方法運行所處的線程??梢钥闯?,只有doInBackground是在異步線程進行執(zhí)行。
創(chuàng)建
讓我們看看它的創(chuàng)建過程,來到它的構(gòu)造函數(shù):
public AsyncTask() {
this((Looper) null);
}
它的無參構(gòu)造函數(shù)轉(zhuǎn)調(diào)到了它的有參構(gòu)造函數(shù),這個構(gòu)造函數(shù)需要以Looper作為一個參數(shù):
public AsyncTask(@Nullable Looper callbackLooper) {
// 根據(jù)傳遞進來的 Looper 來構(gòu)建了一個 Handler,若沒有指定 Looper 或指定的 Looper 是主線程的 Looper,
// 則指定內(nèi)置的 InternalHandler 對消息進行處理(見附1),否則構(gòu)造一個對應(yīng)的 Handler。
// 可以看出 AsyncTask 不是一定回到主線程的。
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// 構(gòu)建了一個 WorkerRunnable,WorkerRunnable 實際上是對 Callable 的簡單包裝(見附2),
// 區(qū)別僅僅在于可以傳入?yún)?shù)。
mWorker = new WorkerRunnable<Params, Result>() {
// 當 mWorker 被執(zhí)行時
public Result call() throws Exception {
// 將當前 Task 設(shè)置為被執(zhí)行
mTaskInvoked.set(true);
Result result = null;
try {
// 進行線程優(yōu)先級的設(shè)置
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 調(diào)用 doInBackground 方法并獲取了返回值
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 看來這個 WorkerRunnable 是在異步線程中執(zhí)行的。
// 不論成功與否,它最后都會調(diào)用 postResult 進行結(jié)果的交付。
postResult(result);
}
// 返回結(jié)果
return(result);
}
};
// 基于前面的 mWorker 構(gòu)建了一個 FutureTask
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
// 當執(zhí)行完成或被取消時,會調(diào)用 postResultIfNotInvoked 方法傳入 mWorker 的執(zhí)行結(jié)果。
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) {
// 當執(zhí)行完成或被取消時,會調(diào)用 postResultIfNotInvoked 方法傳入 mWorker 的執(zhí)行結(jié)果。
postResultIfNotInvoked(null);
}
}
};
}
附1
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return(sHandler);
}
}
附2
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
總的來說,其構(gòu)造過程主要是對 Handler、WorkerRunnable 以及 FutureTask 進行了構(gòu)建。
執(zhí)行
讓我們看看當調(diào)用execute之后它做了什么:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params...params) {
return(executeOnExecutor(sDefaultExecutor, params));
}
可以看到,它轉(zhuǎn)調(diào)到了executeOnExecutor方法,并且傳入了一個Executor對象sDefaultExecutor,我們先不去關(guān)注這個Executor的設(shè)計,讓我們先看看executeOnExecutor方法:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params...params) {
// 首先,它對當前 Task 的狀態(tài)進行了檢查,AsyncTask 共有三種狀態(tài)(見附1)
// 只有 PENDING 的 Task 才能被 execute。
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)");
}
}
// 改變當前 Task 的狀態(tài)
mStatus = Status.RUNNING;
// 調(diào)用 onPreExecute 方法進行了用戶實現(xiàn)的預(yù)處理
onPreExecute();
// 將用戶的參數(shù)交給 mWorker
mWorker.mParams = params;
// 將 mFuture 交給了傳入的線程池進行處理
exec.execute(mFuture);
return(this);
}
附1
public enum Status {
// 待執(zhí)行
PENDING,
// 正在執(zhí)行
RUNNING,
// 已執(zhí)行
FINISHED,
}
FutureTask執(zhí)行后,會使得mWorker被執(zhí)行,它執(zhí)行后會將mTaskInvoked.set(true),并調(diào)用postResult進行結(jié)果的交付:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return(result);
}
這里實際上就是把結(jié)果包裝成了AsyncTaskResult類并放入了消息隊列,這樣就實現(xiàn)了線程的切換,將消息通過Handler由子線程發(fā)送給了主線程(指AsyncTask被指定的線程),當Handler收到消息后,就會對消息進行處理。
回到AsyncTask創(chuàng)建的時候,我們可以看到在構(gòu)造時沒有指定Looper的情況下,默認的InternalHandler是如何進行處理的:
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return(sHandler);
}
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
// 在 MESSAGE_POST_RESULT 消息下,它在收到 AsyncTaskResult 后會調(diào)用 finish 方法進行整個任務(wù)
// 的完成處理(見附1)
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;
}
}
}
附1
private void finish(Result result) {
// 若任務(wù)已取消,它會調(diào)用 onCancelled 方法進行回調(diào)
if (isCancelled()) {
onCancelled(result);
}
// 若任務(wù)正常完成,它會調(diào)用 onPostExecute 方法
else {
onPostExecute(result);
}
// 設(shè)置狀態(tài)為 FINISHED
mStatus = Status.FINISHED;
}
當 FutureTask 執(zhí)行完成后,會調(diào)用postResultIfNotInvoked方法,并傳入mWorker的執(zhí)行結(jié)果:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
可以看到,這里實際上是在Task沒有被Invoke的情況下才會調(diào)用到,由于mWorker在被執(zhí)行時首先就會將wasTaskInvoked置為 true,因此實際上這里很少能被調(diào)用到。
我們再看看onProgressUpdated何時會被調(diào)用到,我們可以找到MESSAGE_POST_PROGRESS的消息是何時被發(fā)出的:
@WorkerThread
protected final void publishProgress(Progress...values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values )).sendToTarget();
}
}
它是在publishProgress方法中被調(diào)用的,這個方法是提供給用戶的,因此進度的更新需要用戶自行在doInBackground中調(diào)用publishProgress從而實現(xiàn)。
取消
我們看看任務(wù)的取消,看到cancel方法:
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
// cancel 方法最后會調(diào)用到 mFuture 的 cancel,并且它需要傳遞一個參數(shù) mayInterruptIfRunning,
// 見附1
return(mFuture.cancel(mayInterruptIfRunning));
}
附1
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
U.compareAndSwapInt(this, STATE, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return(false);
try {
// 當 mayInterruptIfRunning 為 true 時,即使任務(wù)在運行時也會被打斷。
// 而如果為 false,則 doInBackground 仍然會一直執(zhí)行到結(jié)束。
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally {
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return(true);
}
這個設(shè)計非常奇怪,按道理來說取消的場景只有任務(wù)正在執(zhí)行時需要取消,完全可以提供一個這個參數(shù)默認為 true 的方法供外部調(diào)用。并且這個方法只能盡量讓任務(wù)盡快結(jié)束,如果執(zhí)行的過程中有一些不可打斷的操作,則這個方法調(diào)用后仍然不會使得任務(wù)停止。
線程池
對于AsyncTask,我們還有最后一個問題沒有研究,那就是它執(zhí)行任務(wù)的sDefaultExecutor:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
可以看到它的默認值為SERIAL_EXECUTOR。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
SERIAL_EXECUTOR是SerialExecutor的實例。
private static class SerialExecutor implements Executor {
// Executor 內(nèi)部維護了一個 ArrayDeque 隊列,這個隊列是一個任務(wù)緩存隊列,
// 里面存放了對真正要執(zhí)行的 Runnable 進行包裝的 Runnable。
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();
}
}
} );
// 當 mActive 為 null,它會調(diào)用 scheduleNext 方法。
if (mActive == null) {
scheduleNext();
}
}
// 在 scheduleNext 方法中會從 mTasks 隊首取出任務(wù)賦值給 mActive,
// 之后通過 THREAD_POOL_EXECUTOR 這個線程池對該任務(wù)進行執(zhí)行。
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
當任務(wù)執(zhí)行完成后,會繼續(xù)調(diào)用scheduleNext對隊列中的下一個任務(wù)進行執(zhí)行。通過這樣的設(shè)計,Runnable的執(zhí)行變成了一種按序執(zhí)行,只有前一個執(zhí)行結(jié)束,才會進行下一個任務(wù)的執(zhí)行,因此AsyncTask的執(zhí)行順序?qū)嶋H上是一種串行執(zhí)行。
那么THREAD_POOL_EXECUTOR又是一個怎樣的線程池呢?讓我們看看它的聲明:
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int KEEP_ALIVE_SECONDS = 3;
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
可以看到,THREAD_POOL_EXECUTOR的配置如下:
- CORE_POOL_SIZE:內(nèi)部核心線程個數(shù)
- MAXIMUM_POOL_SIZE:最大線程數(shù)量
- KEEP_ALIVE_SECONDS:每一個線程空閑后等待的時間,時間到了就會被停止
只保留一個池線程。如果需要,讓池增長到相當多的線程,但讓他們很快超時。 在不太可能發(fā)生線程用盡的情況下,退回到一個簡單的無界隊列執(zhí)行程序。
這種組合可確保:
- 保持少量的線程
- 僅在啟動明顯更大,但仍有限的線程集之后才進行排隊
- 將線程總數(shù)限制為有界,但仍然允許無限制的任務(wù)集排隊。
不足之處
AsyncTask看似十分美好,但實際上存在著非常多的不足,這些不足使得它在 API 30被廢棄:
- 生命周期:
AsyncTask沒有與Activity、Fragment的生命周期綁定,即使Activity被銷毀,它的doInBackground任務(wù)仍然會繼續(xù)執(zhí)行。 - 取消任務(wù):
AsyncTask的cancel方法的參數(shù)mayInterruptIfRunning存在的意義不大,并且它無法保證任務(wù)一定能取消,只能盡快讓任務(wù)取消(比如,如果正在進行一些無法打斷的操作時,任務(wù)就仍然會執(zhí)行) - 內(nèi)存泄漏:由于它沒有與
Activity等生命周期進行綁定,因此它的生命周期仍然可能比Activity長,如果將它作為Activity的非static內(nèi)部類,則它會持有Activity的引用,導致Activity的內(nèi)存無法釋放。
總結(jié)
本文基本上對AsyncTask的實現(xiàn)原理有一個大致的了解了,它的原理實際上還是挺簡單的:通過Executor與FutureTask配合,從而實現(xiàn)任務(wù)的異步執(zhí)行,最后在任務(wù)結(jié)束后通過Handler進行線程的切換從而實現(xiàn)了整個線程調(diào)度的功能。在Android 3.0之后,默認情況下AsyncTask的執(zhí)行順序是串行進行的。
? 2020 Tyhoo Wu, All rights reserved.