Android源碼系列五: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.)

AsyncTask 是用來幫助我們更好的使用 ThreadHandler,最好是用於處理短時間異步任務。

使用 AsyncTask 解決異步問題

// 實現AsyncTask的子類
class MyAsyncTask(val name: String) : AsyncTask<Params, Progress, Result>() {

    // 執行任務之前的準備工作,比如將進度條設置爲Visible,工作在主線程
    override fun onPreExecute() {
        Log.d("async", "onPreExecute")
    }

    // 在onPreExecute()執行完之後立即在後臺線程中調用
    override fun doInBackground(vararg params: String?): Any? {
        Log.d("async", "$name execute")
        Thread.sleep(1000)
        publishProgress(1)
        return null
    }

    // 調用了publishProgress()之後,會在主線程中被調用,用於更新整體進度
    override fun onProgressUpdate(vararg values: Int?) {
        Log.d("async", "progress is: $values")
    }
    
    // 後臺線程執行結束後,會把結果回調到這個方法中,並在主線程中被調用
    override fun onPostExecute(result: Any?) {
        Log.d("async", "onPostExecute")
    }
}

// 調用
MyAsyncTask("one").execute("")
MyAsyncTask("two").execute("")
MyAsyncTask("three").execute("")
MyAsyncTask("four").execute("")

// 結果
19:29:01.472 1786-1805/com.taonce D/async: one execute
19:29:02.514 1786-1832/com.taonce D/async: two execute
19:29:03.868 1786-1833/com.taonce D/async: three execute
19:29:04.871 1786-1833/com.taonce D/async: four execute

AsyncTask 必須被子類實例化才能使用,其中必須要實現的方法是:doInBackground(vararg params: String?): Any? , 其餘三個方法是可選取實現的,具體的解釋在上面的代碼中都解釋過了。還剩下三個泛型參數,下面一一來解釋下:

  • Params:執行任務所需要的信息;
  • Progress:執行任務的時候,對外發布的進度;
  • Result:任務執行完成後的結果。

到這爲止,你是否發現了一個現象,在結果打印的信息中,每個日誌都是相隔1s,仔細看看是不是,暫且留下一個坑放着,後面會分析給大家聽。下面開始進入源碼分析:

構造函數

先簡單的分析下構造函數中的代碼:

public AsyncTask(@Nullable Looper callbackLooper) {
    // 初始化mHandler,如果callbackLooper爲空或者爲sMainLooper,那就使用getMainHandler()的Handler
    // 否則使用傳進來的callbackLooper
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
    // 初始化一個WorkerRunnable對象,它是Callable的實現類,它的call()方法可以擁有返回值
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            ...
        }
    };
    // 初始化FutureTask對象,它傳入了mWorker對象
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            ...
        }
    };
}

mWorkermFuture 暫時不分析,等到調用的時候我們再結合源碼看,目前我們只需要知道初始化了這些對象就夠了。

execute()

接着我們來看看執行一個任務的入口 execute()

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    // 調用executeOnExecutor(executor,params),傳入的是sDefaultExecutor
    return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            // 這裏解釋了爲什麼不能重複調用execute()方法
            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)");
        }
    }
    // 將狀態標記爲Running
    mStatus = Status.RUNNING;
    // 調用onPreExecute(),它是最先調用的
    onPreExecute();
    // 將params傳給mWorker
    mWorker.mParams = params;
    // 執行sDefaultExecutor.execute(runnable)方法
    exec.execute(mFuture);
    // 返回自身
    return this;
}

我們調用 AsyncTask.execute(parasm) 方法其實就是調用了 sDefaultExecutor.execute(mFuture) 方法,我們順藤摸瓜下去。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

private static class SerialExecutor implements Executor {
    // 雙端隊列
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        // 添加到隊列中,並且執行runnable.run()
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    // 執行完r.run()之後依舊會調用一次scheduleNext()方法
                    // 也就是循環的取隊列中的Runnable,直到Runnable爲空
                    // 從這裏就可以看出,任務都是按照串行的形式來執行的,必須等待一個任務執行完畢纔去取隊列中的下一個,也就解釋了開頭的每個日誌打印相隔1s的原因。
                    scheduleNext();
                }
            }
        });
        // mActive第一次必然爲空
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        // 從隊列中取出Runnable,並且讓線程池去執行
        // THREAD_POOL_EXECUTOR是一個線程池,配置在源碼的最上方
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

SerialExecutor 就是一個實現了 Executor 的靜態內部類,它的 execute(runnable) 接收 Runnable 對象,然後在 executeOnExecutor(Executor exec, Params... params) 方法中傳入的是 mFuture 對象,所以在執行到 scheduleNext() 的時候,就會調用 mFuturerun() 方法。

FutureTask.run()

public void run() {
    ...
    try {
        // 拿到Callable對象
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                // 執行Callable.call()方法
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                // 將Callable.call()方法set()起來,可通過get()獲取
                set(result);
        }
    }...
}

FutureTask.run() 方法就是調用它的 Callable 變量的 call() 方法,在 AsyncTask 中,傳入的 Callable 就是 mWorker 對象,這時候我們就可以把構造函數中的 mWorker 初始化的代碼搬出來讀一讀了。

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Result result = null;
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            // 獲取doInBackground(params)的返回值
            result = doInBackground(mParams);
            Binder.flushPendingCommands();
        } catch (Throwable tr) {
            // 出現異常將mCancelled設置爲true
            mCancelled.set(true);
            throw tr;
        } finally {
            // 回調結果
            postResult(result);
        }
        return result;
    }
};

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

mWorker 其實很簡單,就是拿到 doInBackground(params) 的返回值,無論是否出現異常,都將結果回調到主線程,回調的操作看下面的 postResult(result)

private Result postResult(Result result) {
    // getHandler()就是獲取mHandler對象,默認綁定的是主線程Looper
    // 而mHandler在構造函數中已經解釋過,拿到的就是sHandler
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

通過 getHandler().obtainMessage()獲取一個 Message 對象,將消息的 what 設置爲 MESSAGE_POST_RESULT 標記爲結果不是進度,obj 設置爲 AsyncTaskResult 對象,它是一個內部類,包含了 AsyncTaskData[] 兩個參數。通過 sHandler 將消息發送出去,然後我們看看它是怎麼將 onPostExecute(result) 聯繫起來的。

sHandlerInternalHandler 的實例,我們從 InternalHandler 的源碼中就能得到想要的答案:

    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:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

InternalHandler 判斷了消息的類型,一種是結果,一種是進度,如果是結果的話,那麼就調用 AsyncTaskfinish() 方法;如果是進度的話,那麼就調用 onProgressUpdate() 方法,這個方法在前面就已經介紹過了,就是更新整體進度的方法,我們只要看下 finish() 方法就ok了:

    private void finish(Result result) {
        // 任務是否被取消
        if (isCancelled()) {
            // 任務被取消的話,那麼就調用onCancel(),這個方法也是可以重寫的
            onCancelled(result);
        } else {
            // 沒有被取消就去調用onPostExecute()
            onPostExecute(result);
        }
        // 將狀態置爲FINISHED
        mStatus = Status.FINISHED;
    }

源碼分析到此的話,大致的流程已經完成了,通過 execute() 方法將需要執行的任務添加到 SerialExecutor 的雙端隊列中,然後讓 THREAD_POOL_EXECUTOR 線程池去依次執行隊列中的 FutureTask.run() ,這裏需要注意,執行任務是一個一個執行,執行結束之後再去執行另外一個,這個線程池相當於單線程模式,以串行的形式來執行任務的,而不是並行來執行,整個線程池中真正工作的只有那一個核心線程。通過執行 FutureTask.run() 方法去間接執行 mWorker.call() 方法,在 call() 方法中獲取 doInBackground() 方法返回的結果,最後通過 postResult()InternalHandler 來將結果回調到 onPostExecute() 方法中。

源碼分析的文章還在不斷的更新,如果本文章你發現有不正確或者不足之處,歡迎你在下方留言或者掃描下方的二維碼留言也可!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章