AsyncTask 是一個較爲輕量級的異步任務類,在底層通過封裝 ThreadPool 和 Handler ,實現了線程的複用,後臺任務執行順序的控制、子線程和 UI 線程的切換,使得開發者可以以簡單的方法來執行一些耗時任務
此篇文章就基於 Android API 27 版本的源碼來對 AsyncTask 進行一次整體分析,以便對其底層工作流程有所瞭解
一般,AsyncTask 是以類似於以下的方式來調用的
new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... strings) {
return null;
}
}.execute("leavesC");
所以這裏就從 execute()
方法入手
//以默認的串行任務執行器 sDefaultExecutor 來執行後臺任務
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute(Params)
方法內部調用的是 executeOnExecutor(sDefaultExecutor, params)
方法,當中 sDefaultExecutor
用於定義任務隊列的執行方式,AsyncTask 默認使用的是串行任務執行器
//以指定的任務執行器 Executor 來執行後臺任務
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
//Task 只能被執行一次,如果 mStatus != Status.PENDING ,說明 Task 被重複執行,此時將拋出異常
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;
//在 doInBackground() 方法之前被調用,用於做一些界面層的準備工作
onPreExecute();
//執行耗時任務
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
mStatus
是一個枚舉變量,用於定義當前 Task 的運行狀態,用於防止 Task 被重複執行
//用於標記 Task 的當前狀態
public enum Status {
//Task 還未運行
PENDING,
//Task 正在運行
RUNNING,
//Task 已經結束
FINISHED,
}
之後就調用任務執行器,提交任務
//執行耗時任務
mWorker.mParams = params;
exec.execute(mFuture);
executeOnExecutor(Executor, Params)
方法可以從外部傳入自定義的任務執行器對象,例如可以傳入 AsyncTask.THREAD_POOL_EXECUTOR 使 AsyncTask 中的任務隊列以並行的方式來完成
這裏先來看下默認的串行任務執行器是如何執行的
每一個被提交的任務都會被加入任務隊列 mTasks
當中,mActive
表示當前在執行的任務,每當有新任務 Runnable
到來時,就會在 Runnable
的外層多包裹一層 Runnable
,然後將之插入到任務隊列中,當 execute(Runnable)
方法第一次被執行時,mActive
爲 null ,因此就會觸發 scheduleNext()
方法獲取任務隊列的第一個任務並提交給線程池 THREAD_POOL_EXECUTOR
進行處理,當 r.run()
方法返回時(即任務處理結束),在 finally
中又會獲取下一個任務進行處理,從而實現了任務隊列的串行執行
//串行任務執行器,即提交給線程池的任務是按照順序一個接一個被執行的
private static class SerialExecutor implements Executor {
//任務隊列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
//當前在執行的任務
Runnable mActive;
public synchronized void execute(final Runnable r) {
//向任務隊列尾端插入任務
//在外部任務外部包裝多一層 Runnable
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);
}
}
}
再看下線程池 THREAD_POOL_EXECUTOR
是如何定義的
可以看到,具體的線程池實現類是 ThreadPoolExecutor
,使用線程池從而避免了線程重複的創建與銷燬操作,有利於提高系統性能
//CPU 核數量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//線程池中的核心線程數
//至少有2個,最多4個,線程數至少要比 CPU 核數量少1個,以避免 CPU 與後臺工作飽和
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;
//線程在閒置時的存活時間(30秒),超出這個時間將被回收
private static final int KEEP_ALIVE_SECONDS = 30;
//線程隊列
//當 LinkedBlockingDeque 已滿時,新增的任務會直接創建新線程來執行,當創建的線程數量超過最大線程數量 KEEP_ALIVE_SECONDS 時會拋出異常
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
//線程工廠,提供創建新線程的功能,通過線程工廠可以對線程的一些屬性進行定製
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());
}
};
//線程池對象
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);
//包括核心線程在內的所有線程在閒置時間超出 KEEP_ALIVE_SECONDS 後都將其回收
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
//當前 Task 使用的任務執行器
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
看到線程池,這裏就又引出了另外一個問題,後臺任務是在子線程中調用的,那 AsyncTask 又是如何在 UI 線程中回調 onPreExecute()、onPostExecute(Result)、onProgressUpdate(Progress)
這幾個方法的呢?
先看幾個相關方法的聲明
//在子線程中被調用,用於執行後臺任務
@WorkerThread
protected abstract Result doInBackground(Params... params);
//在 UI 線程中被調用,在 doInBackground() 方法之前調用,用於在後臺任務開始前做一些準備工作
@MainThread
protected void onPreExecute() {
}
//在 UI 線程中被調用,在 doInBackground() 方法之後調用,用於處理後臺任務的執行結果
//參數 result 是 doInBackground() 方法的返回值
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
//在 UI 線程中被調用,當調用了 publishProgress() 方法後被觸發
//用於更新任務進度值
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//在 UI 線程中被調用
//當調用了 cancel(boolean) 方法取消後臺任務後會被調用
//在 doInBackground() 方法結束時也會被調用
//方法內部默認調用了 onCancelled() 方法
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
onCancelled();
}
//在 UI 線程中被調用,被 onCancelled(Result) 方法調用
@MainThread
protected void onCancelled() {
}
onPreExecute()
在 executeOnExecutor(Executor, Params)
中有被調用,因爲 executeOnExecutor()
方法被要求在 UI 線程中調用,因此 onPreExecute()
自然也會在 UI 線程中被執行
其它方法的調用則涉及到了 Handler、Looper 與 MessageQueue 的相關知識點,關於這些可以從這裏獲取詳細介紹: Java_Android_Learn ,這裏就簡單介紹下
看下 AsyncTask 類的三個構造函數。當中,除了無參構造函數,其他兩個構造函數都使用 @hide
註解隱藏起來了,因此我們在一般情況下只能使用調用無參構造函數來初始化 AsyncTask
//創建一個新的異步任務,必須在UI線程上調用此構造函數
public AsyncTask() {
this((Looper) null);
}
/**
* 隱藏的構造函數
* 創建一個新的異步任務,必須在UI線程上調用此構造函數
*
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* 隱藏的構造函數
* 創建一個新的異步任務,必須在UI線程上調用此構造函數
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
//如果 callbackLooper 爲 null 或者是等於主線程 Looper ,則以主線程 Looper 對象爲參數構建一個與主線程關聯的 Handler 對象
//否則就以傳入的 Looper 對象爲參數來構建與子線程關聯的 Handler
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return 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);
}
}
};
}
因此我們傳給構造函數 AsyncTask(Looper)
的參數爲 null ,因爲 mHandler 變量其實是賦值爲綁定了 UI 線程 Looper 的 InternalHandler 變量
因爲 InternalHandler 綁定了 UI 線程的 Looper 對象,因此 handleMessage(Message)
方法其實是在 UI 線程被執行,從而實現了子線程和 UI 線程之間的切換
//按照正常情況來說,在初始化 AsyncTask 時我們使用的都是其無參構造函數
//因此 InternalHandler 綁定的 Looper 對象即是與主線程關聯的 Looper 對象
//所以 InternalHandler 可以用來在 UI 線程回調某些抽象方法,例如 onProgressUpdate() 方法
private static InternalHandler sHandler;
//等於 sHandler
private final Handler mHandler;
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) {
case MESSAGE_POST_RESULT:
//處理後臺任務的執行結果
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//更新後臺任務的進度
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
//獲取與主線程關聯的 Looper 對象,以此爲參數構建一個 Handler 對象
//所以在 Task 的運行過程中,能夠通過此 Handler 在 UI 線程執行操作
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
例如,在通過 publishProgress(Progress)
方法更新後臺任務的執行進度時,在內部就會將進度值包裝到 Message 中,然後傳遞給 Handler 進行處理
//運行於工作線程,此方法用於更新任務的進度值
//會觸發 onProgressUpdate() 被執行
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
//將與進度值相關的參數 Progress 包裝到 AsyncTaskResult 對象當中,並傳遞給 Handler 進行處理
getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
以上就是 AsyncTask 較爲關鍵的幾個點,看過後應該就能明白 AsyncTask 的整體工作流程了,如果需要 AsyncTask 更爲詳細的源碼註釋,可以看這裏:AsyncTask
更多的源碼解讀請看這裏:Java_Android_Learn