本系列文章會陸續對 Android 的多線程機制進行總體介紹,幫助讀者瞭解 Android 環境下如何實現多線程編程,也算是對本身所學內容的一個總結概括java
項目主頁:github.com/leavesC/Jav…android
AsyncTask 是一個較爲輕量級的異步任務類,在底層經過封裝 ThreadPool 和 Handler ,實現了線程的複用,後臺任務執行順序的控制、子線程和 UI 線程的切換,使得開發者能夠以簡單的方法來執行一些耗時任務git
此篇文章就基於 Android API 27 版本的源碼來對 AsyncTask 進行一次總體分析,以便對其底層工做流程有所瞭解github
通常,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 被重複執行ide
//用於標記 Task 的當前狀態
public enum Status {
//Task 還未運行
PENDING,
//Task 正在運行
RUNNING,
//Task 已經結束
FINISHED,
}
複製代碼
以後就調用任務執行器,提交任務函數
//執行耗時任務
mWorker.mParams = params;
exec.execute(mFuture);
複製代碼
executeOnExecutor(Executor, Params)
方法能夠從外部傳入自定義的任務執行器對象,例如能夠傳入 AsyncTask.THREAD_POOL_EXECUTOR 使 AsyncTask 中的任務隊列以並行的方式來完成oop
這裏先來看下默認的串行任務執行器是如何執行的
每個被提交的任務都會被加入任務隊列 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 的相關知識點,關於這些能夠從這裏獲取詳細介紹: JavaKotlinAndroidGuide ,這裏就簡單介紹下
看下 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();
}
}
複製代碼
更多的源碼解讀請看這裏:JavaKotlinAndroidGuide