本文很多資料基於Google Developer官方對AsyncTask的最新介紹。
AsyncTask 是什么

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework.
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
上文翻譯:AsyncTask 是一個被設計為圍繞Thread和Handler操作的工具幫助類,而不是作為通用的線程框架,理想情況下,應將AsyncTasks用於短操作(最多幾秒鍾)。如果需要長時間保持線程運行,Google建議使用java.util.concurrent這個並發包提供的各種API,例如 Executor,ThreadPoolExecutor和 FutureTask。
!This class was deprecated in API level 30.
Use the standard java.util.concurrent or Kotlin concurrency utilities instead.
目前官方已經明確說明,AsyncTask 將會在API 30,也就是Android 11的版本中,將這個類廢棄掉。使用java.util.concurrent
和Kotlin的協程組件代替AsyncTask 。
谷歌要將AsyncTask廢棄掉的原因,我猜測是:AsyncTask 是一個很古老的類了,在API Level 3的時候就有了,還有着許多致命的缺點,終於連Google都忍不了,加上目前已經有許多替代的工具了,如Kotlin協程等。
AsyncTask的缺陷
However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes.
It also has inconsistent behavior on different versions of the platform, swallows exceptions from doInBackground, and does not provide much utility over using Executors directly.
譯:
- AsyncTask 最常見是子類繼承然后直接用在 UI層的代碼里,這樣容易導致Context的內存泄漏問題
- Callback回調的失效
- 配置改變時的崩潰
- 不同版本行為不一致:最初的AsyncTasks在單個后台線程上串行執行;Android1.6時它更改為線程池,允許多個任務並行運行;Android 3.2時又改為只會有單個線程在執行請求,以避免並行執行引起的常見應用程序錯誤。
- 在它的重要方法doInBackground中會將出現的異常直接吞掉
- 多個實例調用execute后,不能保證異步任務的執行修改順序合理
- 在直接使用Executors方面沒有提供太多實用性
缺點真的蠻多的,簡單用用可能還行,但是要考慮清楚
AsyncTask的參數和重要方法
定義子類時需設置傳入的三大參數類型<Params , Progress , Result>,如果某類型未用到的將它設為Void
-
Params(在執行AsyncTask時需要傳入的參數,可用於在后台任務中使用)
-
Progress(后台任務執行時,如果需要在界面上顯示當前的進度,則使用這里指定的泛型作為進度單位)
-
Result(當任務執行完畢后,如果需要對結果進行返回,則使用這里指定的泛型作為返回值類型)
定義子類時可重寫四大方法:onPreExecute,onProgressUpdate,doInBackground,onPostExecute
-
onPreExecute()
這個方法會在后台任務開始執行之前調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
-
doInBackground(Params...)
子線程中運行此方法,可以將幾秒的耗時任務在這里運行。任務一旦完成就可以通過return語句來將任務的執行結果進行返回。若Result類型,指定了Void,就不會返回任務執行結果。如果想要更新當前的任務進度想在UI中顯示出來,可以通過 publishProgress(Progress...)。
-
onProgressUpdate(Progress...)
當
doInBackground(params)
調用了publishProgress(Progress...)方法后,此方法中會被調用,傳遞的參數值就是在后台任務中傳遞過來的,類型是上面說到的Progress
的類型。這個方法是在UI線程操作,對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。 -
onPostExecute(Result)
當后台任務執行完畢並通過return語句進行返回時,返回的數據會作為參數傳遞到此方法中,可以利用返回的
Result
來進行一些UI操作。
AsyncTask開始簡單的異步任務
簡單來說:AsyncTask是一個抽象類,必須被子類化才能使用,這個子類一般都會覆蓋這兩個方法doInBackground(Params...)
和 onPostExecute(Result)
,創建AsyncTask子類的實例執行execute方法就運行異步任務了。
//最簡單的AsyncTask實現方式
public class DownloadTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Boolean doInBackground(String... strings) {
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
}
}
//在UI線程中啟用 AsyncTask
new DownloadTask().execute();
使用AsyncTask要遵守的規矩
-
必須在UI線程上加載AsyncTask類。
-
必須在UI線程上創建 AsyncTask子類的實例。
-
必須在UI線程上調用 execute(Params ...)。
-
不要手動調用onPreExecute,onPostExecute,doInBackground,onProgressUpdate這幾個方法
-
該任務只能執行一次(如果嘗試第二次執行,則將引發異常。)
好雞肋的設定啊,不知道當初為什么要這樣設計
AsyncTask源碼分析
先由一行最簡單的啟動AsyncTask的代碼入手:
new DownloadTask().execute("");
進入execute方法查看:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
先看下sDefaultExecutor
這個屬性是什么名堂:
//sDefaultExecutor 被 volatile修飾
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//串行執行任務線程池實例
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
//維護一個Runnable的隊列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
//往隊尾中壓入一個Runnable的同時運行隊頭的Runnable,維護隊列的大小
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
//彈出,執行隊頭的Runnable
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
//執行 被賦值的mActive 任務
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
//創建核心線程數為 1,最大線程容量為20的線程池,實際的任務執行線程池
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;
}
可以看到的是sDefaultExecutor
,是一個控制任務排隊的線程池,被調用execute
時會將新的Runnable壓入任務隊列,如果Runnable mActive == null的話會取出隊頭的Runnable執行,而每當一個任務結束后都會執行任務隊列中隊頭Runnable。
這樣做的目的是:保證在不同情況,只能有一個任務可以被執行,SerialExecutor做出了單一線程池的效果。每當一個任務執行完畢后,下一個任務才會得到執行,假如有這么一種情況,主線程快速地啟動了很多AsyncTask任務,同一時刻只會有一個線程正在執行,其余的均處於等待狀態。
再來看看executeOnExecutor
這個方法又有什么名堂:
@MainThread
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;
//由上文提到的 sDefaultExecutor 執行 FutureTask<Result>任務
exec.execute(mFuture);
return this;
}
//Callable的子類,泛型類型為<Params, Result>
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
executeOnExecutor
執行方法,會先檢查是否運行了這個任務或者已結束,由於AsyncTask任務規定每個任務只能執行一次,不符合就會拋出異常。接着開始調用 onPreExecute
開始預執行,然后給mWorker賦值參數,執行
mFuture蘊含的任務。到這里好像就沒了?非也非也,還有很關鍵的AsyncTask的構造函數👇
/**
* 創建一個新的異步任務。 必須在UI線程上調用此構造函數。
*/
public AsyncTask(@Nullable Looper callbackLooper) {
//獲取主線程Looper的Handler對象
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//Worker線程被運行后會執行異步的任務
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//執行doInBackground(mParams)返回 Result類型
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
//FutureTask--泛型參數為 Result
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);
}
}
};
}
//postResultIfNotInvoked 如果未被調用,傳遞結果
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
//利用Handler將帶有result的消息壓入消息隊列,等待主線程的Handler執行
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//getHandler()獲取到的就是AsyncTask構建對象時創建的‘mHandler’,持有主線程Looper
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
AsyncTask
的構造方法,對三個重要的東西(mHandler,mWorker,mFuture)進行了初始化,確保他們在被運行時能夠執行相應的任務。
再來看下 mHandler
是 InternalHandler
的實例,這個持有主線程Looper的Handler子類的特別之處:
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) {
//result.mTask屬性是AsyncTask的引用
//當接收到不同類型消息,會分別執行 在UI線程更新進度 和 結束任務的方法
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;
}
}
}
由於InternalHandler
的實例被創建時,獲取的是主線程的Looper,所以它的 handleMessage(msg)
方法自然也是在主線程中執行
再看下這個AsyncTask類內的 finish 方法
private void finish(Result result) {
if (isCancelled()) {
//任務已經被取消了,執行onCancelled
onCancelled(result);
} else {
//任務正常結束了,執行onPostExecute(result)
onPostExecute(result);
}
//將任務狀態變更為 已結束
mStatus = Status.FINISHED;
}
判斷任務是由於被取消掉而結束還是正常結束,執行onCancelled(result) or 執行onPostExecute(result)
源碼分析總結
AsyncTask類維護着一個串行執行任務的線程池sDefaultExecutor,一個進程中的所有的 AsyncTask 子類全部在這個線程池中執行。
AysncTask 中的兩個線程池(SerialExecutor
和 THREAD_POOL_EXECUTOR
)和維護着一個持有主線程Looper的 Handler(InternalHandler
),其中線程池 SerialExecutor 保證在不同情況,只能有一個任務可以被執行,THREAD_POOL_EXECUTOR
是核心線程數為1的線程池,負責執行耗時任務,InternalHandler
用於將執行環境從后台工作線程切換到主線程,完成UI更新進度,取消任務和任務結束后的UI操作。