AsyncTask源碼解析

AsyncTask是android爲咱們提供執行異步任務的一個輕量的類,能夠用來處理耗時操做,而且可以很方便的將執行結果返回給主線程。本篇文章將會經過源碼分析來介紹AsyncTask的內部實現原理。html

目錄

  1. 重要的成員變量
  2. AsyncTask構造分析
  3. 兩個線程池
  4. 圖解AsyncTask執行過程
  5. 執行結果是如何被傳遞到主線程
  6. onProgressUpdate()是何時調用

1. 重要的成員變量

AsyncTask裏面幾個重要的成員變量變量分別爲:java

名稱 做用 建立 調用 備註
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建立完畢後而後在以次建立WorkerRunnableFutureTask。下面爲AsyncTask構造源碼:android

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是什麼呢?它是一個線程池,是一個類的成員變量。bash

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內部實際上是有兩個線程池SerialExecutorTHREAD_POOL_EXECUTOR,其中SerialExecutor線程池主要是用於將任務添加到隊列中,而任務真正的執行是在THREAD_POOL_EXECUTOR線程池中。async

4. 圖解AsyncTask執行過程

要想知道執行結果是如何被傳遞到線程中,咱們先搞明白AsyncTask的執行過程。其實讀者從上面的內容中或許能改猜到它的大概執行過程。其實它的執行過程也不復雜咱們能夠結果下面這張圖進行分析:ide

AsyncTask執行流程圖

咱們在使用AsyncTask的時候會先建立對象,而後調用execute()方法傳入參數執行任務:函數

//建立AcyncTask封裝類
TestAsyncTask asyncTask = new TestAsyncTask();
//傳入參數,執行任務
asyncTask.execute(5,6,7);
複製代碼

咱們在經過上面操做執行任務的時候,其實AsyncTask內部作了一下幾個操做:oop

  1. 在構造中建立Handler、WorkerRunnable、FutureTask
  2. executeOnExecutor()中校驗該任務是否在任務棧中執行、或者是否已完成過
  3. 若是該未任務在執行,或者未完成過。將會包裝傳入的參數而後再將FutureTask添加到線程池中調用execute()方法執行異步
  4. SerialExecutor線程池的execute()方法建立Runnable,並添加到隊列中。
  5. scheduleNext()方法取出隊列中的第一個Runnable,加他添加到THREAD_POOL_EXECUTOR線程池中開始執行任務
  6. Runnable調用FutureTask的run()方法執行WorkerRunnable的call()方法
  7. WorkerRunnable的call()方法執行完,SerialExecutor線程池的execute()方法再次調用scheduleNext()執行下個任務。

結合上面的執行流程圖咱們知道,在通過上面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對象發送信息的方式對性能上有起到優化的做用。讀者能夠在文章結尾的參考連接中找到相關的介紹,筆者就不介紹了。

總結

文章到這裏對與AsyncTask的源碼分析也就介紹完了。在AsyncTask中比較重要的成員變量爲:WorkerRunnable、FutureTask已經兩個線程池,可以真正理解AsyncTask的執行過程必定要搞明白他們幾個的調用過程。最後感謝您能在百忙之中抽出時間閱讀這篇文章,下一篇文章將會寫一下HandlerThead和IntentService。

參考

ArrayDeque類的使用詳解

Android線程優先級

剖析Android中進程與線程調度之nice

帶你輕鬆看源碼---AsyncTask(異步任務)

Android Message和obtainMessage的區別

相關文章
相關標籤/搜索