AsyncTask原始碼解析
AsyncTask是android為我們提供執行非同步任務的一個輕量的類,可以用來處理耗時操作,並且能夠很方便的將執行結果返回給主執行緒。本篇文章將會通過原始碼分析來介紹AsyncTask的內部實現原理。
目錄
- 重要的成員變數
- AsyncTask構造分析
- 兩個執行緒池
- 圖解AsyncTask執行過程
- 執行結果是如何被傳遞到主執行緒
- onProgressUpdate()是什麼時候呼叫
1. 重要的成員變數
AsyncTask裡面幾個重要的成員變數變數分別為:
名稱 | 作用 | 建立 | 呼叫 | 備註 |
---|---|---|---|---|
THREAD_POOL_EXECUTOR | 真正執行任務的執行緒池 | 在靜態程式碼塊中被建立 | 在SerialExecutor執行緒池的scheduleNext方法中被呼叫 | 該執行緒成池的核心執行緒數量是根據手機cup核數-1確定的 |
sDefaultExecutor | 內部建立佇列用於儲存非同步任務 | 建立類的成員變數的時候被建立 | 在AsyncTask的execute()中被作為引數傳遞 | SerialExecutor類的scheduleNext方法中會將任務新增到THREAD_POOL_EXECUTOR執行緒池中執行 |
mWorker | 任務最終執行方法,其內部的call方法會呼叫doInBackground()方法 | 在AsyncTask有參構造中建立 | WorkerRunnable在FutureTask的run方法中被呼叫該類的call方法 | 其繼承自Callable方法,一般配合FutureTask使用 |
mFuture | 在其內部會呼叫mWorker的call方法來執行任務 | 在AsyncTask有參構造中建立 | FutureTask在SerialExecutor類的execute方法中被呼叫 | 該成員變數被AsyncTask的executeOnExecutor()中傳遞到SerialExecutor中 |
sHandler | 用於將在結果返回到主執行緒 | 在AsyncTask有參構造中通過呼叫getMainHandler來建立 | 在postResult()中通過複用Message來呼叫 | InternalHandler類的Looper是主執行緒的Looper |
2. AsyncTask構造分析
在分析AsyncTask之前我們先看看他的構造,我們在使用 AsyncTask
經常使用空參構造的方式來建立該物件,這個構造方法內部會呼叫他的有參構造。首先有參會先根據是否有Looper來建立Handler。如果傳入的Looper為空或者傳入的Looper不是主執行緒的Looper,則呼叫 getMainHandler()
來建立Handler;如果是主執行緒的Looper則以此Looper重新new一個Handler。當Handler建立完畢後然後在以次建立 WorkerRunnable
和 FutureTask
。下面為AsyncTask構造原始碼:
public AsyncTask(@Nullable Looper callbackLooper) { //建立Hanlder 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); //獲取非同步執行結果 result = doInBackground(mParams); //將程序中未執行的命令一併送往cup處理 Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { //將處理結果返回到主執行緒 postResult(result); } return result; } }; //FutureTask間接呼叫了WorkerRunnable方法的call方法 //來獲取執行結果 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); } } }; } 複製程式碼
從原始碼中我們可以知道,mHandler實際上是 InternalHandler
, mWorker
內部的call()方法會呼叫 doInBackground
,try塊不管執行結果如何,都會呼叫 postResult()
來呼叫Hanlder傳送訊息,通知主執行緒最Ui更新操作。先有一個問題,call()方法是在哪裡會被呼叫呢?其實是在mFuture內部的run()方法中呼叫 mWorker
他的call方法。具體程式碼讀者可以自行查詢專案原始碼,這裡就不多說了。上面提到的mWorker、mFuture會在 execute()
方法中被呼叫和傳遞,execute()是用於配置和啟動任務的方法,下面為該方法的部分程式碼。
/** *在主執行緒中執行 *可傳入一個或多個引數 */ @MainThread public final AsyncTask。<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } 複製程式碼
3. 兩個執行緒池
executeOnExecutor(sDefaultExecutor, params);
方法將引數params和 sDefaultExecutor
傳入該方法中,並返回一個AsyncTask。這個params我們知道它是我們傳進來的引數,但是sDefaultExecutor是什麼呢?它是一個執行緒池,是一個類的成員變數。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 複製程式碼
既然我們知道sDefaultExecutor是一個執行緒池,也就是 SerialExecutor
這個類。那這個類到底是幹什麼呢?下面為改類的原始碼:
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() { //mTask.pll()刪除佇列中的第一個元素,並返回該元素的值 if ((mActive = mTasks.poll()) != null) { //呼叫執行緒池執行非同步 THREAD_POOL_EXECUTOR.execute(mActive); } } } 複製程式碼
從上面的程式碼我們可以知道,SerialExecutor類中建立一個雙端佇列 ArrayDeque
, 用於儲存非同步任務。他還有 execute()
和 scheduleNext()
方法,execute()內部呼叫了 mTasks.offer
用於將傳入的非同步任務新增到佇列中,然後在呼叫 scheduleNext()方法。scheduleNext()方法呼叫 mTask.poll()
方法取出並刪除第一個元素,最後將取出的元素放到執行緒池中。不知道讀者有沒有發現AsyncTask內部其實是有兩個執行緒池 SerialExecutor
和 THREAD_POOL_EXECUTOR
,其中SerialExecutor執行緒池主要是用於將任務新增到佇列中,而任務真正的執行是在THREAD_POOL_EXECUTOR執行緒池中。
4. 圖解AsyncTask執行過程
要想知道執行結果是如何被傳遞到執行緒中,我們先搞明白AsyncTask的執行過程。其實讀者從上面的內容中或許能改猜到它的大概執行過程。其實它的執行過程也不復雜我們可以結果下面這張圖進行分析:

我們在使用AsyncTask的時候會先建立物件,然後呼叫execute()方法傳入引數執行任務:
//建立AcyncTask封裝類 TestAsyncTask asyncTask = new TestAsyncTask(); //傳入引數,執行任務 asyncTask.execute(5,6,7); 複製程式碼
我們在通過上面操作執行任務的時候,其實AsyncTask內部做了一下幾個操作:
executeOnExecutor()
結合上面的執行流程圖我們知道,在經過上面7個步驟非同步任務一個一個的線上程池中被完成。既然我們知道了AsyncTask的大致執行過程,那麼它是如何將執行結果返回到主執行緒呢?下面我們將會來分析。
5. 執行結果是如何被傳遞到主執行緒
我們知道 doInBackground()
函式是我們的任務具體執行函式。這個函式是在WorkerRunnable的call()函式中被呼叫,從上面的執行過程介紹中我們知道call()方法是在FutureTask的run方法執行的時候被呼叫的。當call()方法在執行完doInBackground()方法得到結果後,會將該結果傳遞給 postResult()
方法:
private Result postResult(Result result) { //obtainMessage方法是從Message池中獲取一個Message物件,避免重複建立。 Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); //傳送訊息 message.sendToTarget(); return result; } 複製程式碼
postResult()方法內程式碼也很簡單,首先它會通過Hanlder(注:從文章開始部分我們可以知道,這個Handler的Looper是主執行緒的Looper)在訊息佇列中獲取一個Message物件,然後將結果和定義的標記包裝到Massage中,最後在通過Message物件呼叫sendToTarget()將訊息發出。既然訊息傳送出去了,那麼訊息是在哪裡執行呢?答案是:在 InternalHandler
類中的 handleMessage()
中被執行。why?因為getHandler()獲取的是Hanlder是我們在文章開始介紹的建構函式中被getMainHandler()賦值的 mHandler
,而getMainHandler()中返回的就是InternalHandler。既然我們知道了訊息在哪裡被處理,那麼我們可以看一看它的具體處理邏輯:
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; } } 複製程式碼
handleMessage()內部有兩個判斷,如果標識是 MESSAGE_POST_RESULT
則將結果傳遞給 finish()
方法。如果標識是 MESSAGE_POST_PROGRESS
則呼叫 onProgressUpdate()
用於更新進度。下面我們先看finish()方法的原始碼:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = AsyncTask.Status.FINISHED; } 複製程式碼
finish()方法會判斷是否取消了該任務,如果使用者呼叫了 cancel()
函式那麼 isCancelled()
返回的就是true,當用戶取消了任務那麼將會回撥onCancelled(result)函式而onPostExecute()則不會呼叫,反之則會呼叫。
6. onProgressUpdate()是什麼時候呼叫
在分析handleMessage()方法的時候我們留了一個小尾巴, MESSAGE_POST_PROGRESS
這個標記訊息在什麼時候發出的?在回答這個問題之前,我們先回憶一下我們在使用 doInBackground()
的時候,是否有在其內部呼叫 publishProgress()
函式來更新進入?回憶到這裡答案就很明顯了:通過Handler發生更新進度訊息的操作是在 publishProgress()
函式中完成的。下面為該函式的原始碼:
@WorkerThread protected final void publishProgress(Progress... values) { //如果任務沒有取消,則發生訊息更新進度 if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } } 複製程式碼
從上面的原始碼我們可以知道,更新進度的訊息是在子執行緒中傳送的,如果該任務沒有被取消那麼就可以發現訊息。這種通過複用Message物件傳送資訊的方式對效能上有起到優化的作用。讀者可以在文章結尾的參考連結中找到相關的介紹,筆者就不介紹了。