【Android】深入解析 AsyncTask 源碼

前言:AsyncTaskAndroid SDK 中提供的一個用于執(zhí)行異步任務(wù)的框架,在 Android 興起的早期被廣泛使用,但如今在 API 30 被棄用,取而代之的是 java.util.concurrentKotlin 協(xié)程。雖然它存在著一些不足,但是我們還是可以嘗試了解一下它的實現(xiàn)原理以及存在的不足。

注:本文以 API 29 的 AsyncTask 源碼進行解析。

功能概述

首先,讓我們來簡單地了解一下它的設(shè)計初衷:

AsyncTask的設(shè)計初衷是能夠幫助用戶方便地完成異步任務(wù)的線程調(diào)度,它對用戶提供了如下幾個接口:

public abstract class AsyncTask<Params, Progress, Result> {
    @WorkerThread
    protected abstract Result doInBackground(Params... params);

    @MainThread
    protected void onPreExecute() {
    }

    @MainThread
    protected void onPostExecute(Result result) {
    }

    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

    @MainThread
    protected void onCancelled(Result result) {
        onCancelled();
    }
}

首先看到它的三個范型參數(shù):

  • Params:代表傳遞給它的參數(shù)類型
  • Progress:代表用于反饋進度的數(shù)據(jù)類型
  • Result:代表異步執(zhí)行的結(jié)果類型

用戶可以通過實現(xiàn)doInBackground方法來編寫在異步線程所要進行的處理;通過onPostExecute方法的重寫實現(xiàn)對異步請求結(jié)果的獲?。煌ㄟ^onPreExecute實現(xiàn)了在doInBackground之前進行一些預(yù)處理;通過onProgressUpdate實現(xiàn)對進度的監(jiān)聽;通過onCancelled實現(xiàn)對任務(wù)中斷的監(jiān)聽。

當我們編寫一個AsyncTask后,只需要調(diào)用它的execute方法即可,而背后的線程調(diào)度的過程都會由它替我們完成,看上去是十分美好的。

同時從上面的代碼中可以看到,每個方法都有被@MainThread的注解標注,這些注解主要的作用是用來標注這個方法運行所處的線程??梢钥闯?,只有doInBackground是在異步線程進行執(zhí)行。


創(chuàng)建

讓我們看看它的創(chuàng)建過程,來到它的構(gòu)造函數(shù):

public AsyncTask() {
    this((Looper) null);
}

它的無參構(gòu)造函數(shù)轉(zhuǎn)調(diào)到了它的有參構(gòu)造函數(shù),這個構(gòu)造函數(shù)需要以Looper作為一個參數(shù):

public AsyncTask(@Nullable Looper callbackLooper) {
    // 根據(jù)傳遞進來的 Looper 來構(gòu)建了一個 Handler,若沒有指定 Looper 或指定的 Looper 是主線程的 Looper,
    // 則指定內(nèi)置的 InternalHandler 對消息進行處理(見附1),否則構(gòu)造一個對應(yīng)的 Handler。
    // 可以看出 AsyncTask 不是一定回到主線程的。
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
           ? getMainHandler()
           : new Handler(callbackLooper);
  
    // 構(gòu)建了一個 WorkerRunnable,WorkerRunnable 實際上是對 Callable 的簡單包裝(見附2),
    // 區(qū)別僅僅在于可以傳入?yún)?shù)。
    mWorker = new WorkerRunnable<Params, Result>() {
        // 當 mWorker 被執(zhí)行時
        public Result call() throws Exception {
            // 將當前 Task 設(shè)置為被執(zhí)行
            mTaskInvoked.set(true);
            Result result = null;
            try {
                // 進行線程優(yōu)先級的設(shè)置
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                // 調(diào)用 doInBackground 方法并獲取了返回值
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                // 看來這個 WorkerRunnable 是在異步線程中執(zhí)行的。
                // 不論成功與否,它最后都會調(diào)用 postResult 進行結(jié)果的交付。
                postResult(result);
            }
            // 返回結(jié)果
            return(result);
        }
    };

    // 基于前面的 mWorker 構(gòu)建了一個 FutureTask
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                // 當執(zhí)行完成或被取消時,會調(diào)用 postResultIfNotInvoked 方法傳入 mWorker 的執(zhí)行結(jié)果。
                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) {
                // 當執(zhí)行完成或被取消時,會調(diào)用 postResultIfNotInvoked 方法傳入 mWorker 的執(zhí)行結(jié)果。
                postResultIfNotInvoked(null);
            }
        }
    };
}
附1
private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return(sHandler);
    }
}
附2
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

總的來說,其構(gòu)造過程主要是對 Handler、WorkerRunnable 以及 FutureTask 進行了構(gòu)建。


執(zhí)行

讓我們看看當調(diào)用execute之后它做了什么:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params...params) {
    return(executeOnExecutor(sDefaultExecutor, params));
}

可以看到,它轉(zhuǎn)調(diào)到了executeOnExecutor方法,并且傳入了一個Executor對象sDefaultExecutor,我們先不去關(guān)注這個Executor的設(shè)計,讓我們先看看executeOnExecutor方法:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                    Params...params) {
    // 首先,它對當前 Task 的狀態(tài)進行了檢查,AsyncTask 共有三種狀態(tài)(見附1)
    // 只有 PENDING 的 Task 才能被 execute。
    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)");
        }
    }

    // 改變當前 Task 的狀態(tài)
    mStatus = Status.RUNNING;

    // 調(diào)用 onPreExecute 方法進行了用戶實現(xiàn)的預(yù)處理
    onPreExecute();

    // 將用戶的參數(shù)交給 mWorker
    mWorker.mParams = params;
    // 將 mFuture 交給了傳入的線程池進行處理
    exec.execute(mFuture);

    return(this);
}
附1
public enum Status {
    // 待執(zhí)行
    PENDING,
    // 正在執(zhí)行
    RUNNING,
    // 已執(zhí)行
    FINISHED,
}

FutureTask執(zhí)行后,會使得mWorker被執(zhí)行,它執(zhí)行后會將mTaskInvoked.set(true),并調(diào)用postResult進行結(jié)果的交付:

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                              new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return(result);
}

這里實際上就是把結(jié)果包裝成了AsyncTaskResult類并放入了消息隊列,這樣就實現(xiàn)了線程的切換,將消息通過Handler由子線程發(fā)送給了主線程(指AsyncTask被指定的線程),當Handler收到消息后,就會對消息進行處理。

回到AsyncTask創(chuàng)建的時候,我們可以看到在構(gòu)造時沒有指定Looper的情況下,默認的InternalHandler是如何進行處理的:

private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return(sHandler);
    }
}
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) {
                // 在 MESSAGE_POST_RESULT 消息下,它在收到 AsyncTaskResult 后會調(diào)用 finish 方法進行整個任務(wù)
                // 的完成處理(見附1)
                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;
        }
    }
}
附1
private void finish(Result result) {
    // 若任務(wù)已取消,它會調(diào)用 onCancelled 方法進行回調(diào)
    if (isCancelled()) {
        onCancelled(result);
    } 
    // 若任務(wù)正常完成,它會調(diào)用 onPostExecute 方法
    else {
        onPostExecute(result);
    }
  
    // 設(shè)置狀態(tài)為 FINISHED
    mStatus = Status.FINISHED;
}

當 FutureTask 執(zhí)行完成后,會調(diào)用postResultIfNotInvoked方法,并傳入mWorker的執(zhí)行結(jié)果:

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

可以看到,這里實際上是在Task沒有被Invoke的情況下才會調(diào)用到,由于mWorker在被執(zhí)行時首先就會將wasTaskInvoked置為 true,因此實際上這里很少能被調(diào)用到。

我們再看看onProgressUpdated何時會被調(diào)用到,我們可以找到MESSAGE_POST_PROGRESS的消息是何時被發(fā)出的:

@WorkerThread
protected final void publishProgress(Progress...values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values )).sendToTarget();
    }
}

它是在publishProgress方法中被調(diào)用的,這個方法是提供給用戶的,因此進度的更新需要用戶自行在doInBackground中調(diào)用publishProgress從而實現(xiàn)。


取消

我們看看任務(wù)的取消,看到cancel方法:

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    // cancel 方法最后會調(diào)用到 mFuture 的 cancel,并且它需要傳遞一個參數(shù) mayInterruptIfRunning,
    // 見附1
    return(mFuture.cancel(mayInterruptIfRunning));
}
附1
public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
           U.compareAndSwapInt(this, STATE, NEW,
                    mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return(false);
    try {
            // 當 mayInterruptIfRunning 為 true 時,即使任務(wù)在運行時也會被打斷。
            // 而如果為 false,則 doInBackground 仍然會一直執(zhí)行到結(jié)束。
            if (mayInterruptIfRunning) {
                    try {
                            Thread t = runner;
                            if (t != null)
                                    t.interrupt();
                    } finally {
                            U.putOrderedInt(this, STATE, INTERRUPTED);
                    }
            }
    } finally {
        finishCompletion();
    }
    return(true);
}

這個設(shè)計非常奇怪,按道理來說取消的場景只有任務(wù)正在執(zhí)行時需要取消,完全可以提供一個這個參數(shù)默認為 true 的方法供外部調(diào)用。并且這個方法只能盡量讓任務(wù)盡快結(jié)束,如果執(zhí)行的過程中有一些不可打斷的操作,則這個方法調(diào)用后仍然不會使得任務(wù)停止。


線程池

對于AsyncTask,我們還有最后一個問題沒有研究,那就是它執(zhí)行任務(wù)的sDefaultExecutor

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

可以看到它的默認值為SERIAL_EXECUTOR。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

SERIAL_EXECUTORSerialExecutor的實例。

private static class SerialExecutor implements Executor {
    // Executor 內(nèi)部維護了一個 ArrayDeque 隊列,這個隊列是一個任務(wù)緩存隊列,
    // 里面存放了對真正要執(zhí)行的 Runnable 進行包裝的 Runnable。
    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();
                      }
        }
    } );

        // 當 mActive 為 null,它會調(diào)用 scheduleNext 方法。
        if (mActive == null) {
            scheduleNext();
        }
    }

    // 在 scheduleNext 方法中會從 mTasks 隊首取出任務(wù)賦值給 mActive,
    // 之后通過 THREAD_POOL_EXECUTOR 這個線程池對該任務(wù)進行執(zhí)行。
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

當任務(wù)執(zhí)行完成后,會繼續(xù)調(diào)用scheduleNext對隊列中的下一個任務(wù)進行執(zhí)行。通過這樣的設(shè)計,Runnable的執(zhí)行變成了一種按序執(zhí)行,只有前一個執(zhí)行結(jié)束,才會進行下一個任務(wù)的執(zhí)行,因此AsyncTask的執(zhí)行順序?qū)嶋H上是一種串行執(zhí)行。

那么THREAD_POOL_EXECUTOR又是一個怎樣的線程池呢?讓我們看看它的聲明:

private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int KEEP_ALIVE_SECONDS = 3;

public static final Executor THREAD_POOL_EXECUTOR;

static {
  ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), sThreadFactory);
  threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
  THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

可以看到,THREAD_POOL_EXECUTOR的配置如下:

  • CORE_POOL_SIZE:內(nèi)部核心線程個數(shù)
  • MAXIMUM_POOL_SIZE:最大線程數(shù)量
  • KEEP_ALIVE_SECONDS:每一個線程空閑后等待的時間,時間到了就會被停止

只保留一個池線程。如果需要,讓池增長到相當多的線程,但讓他們很快超時。 在不太可能發(fā)生線程用盡的情況下,退回到一個簡單的無界隊列執(zhí)行程序。

這種組合可確保:

  • 保持少量的線程
  • 僅在啟動明顯更大,但仍有限的線程集之后才進行排隊
  • 將線程總數(shù)限制為有界,但仍然允許無限制的任務(wù)集排隊。

不足之處

AsyncTask看似十分美好,但實際上存在著非常多的不足,這些不足使得它在 API 30被廢棄:

  • 生命周期:AsyncTask沒有與Activity、Fragment的生命周期綁定,即使Activity被銷毀,它的doInBackground任務(wù)仍然會繼續(xù)執(zhí)行。
  • 取消任務(wù):AsyncTaskcancel方法的參數(shù)mayInterruptIfRunning存在的意義不大,并且它無法保證任務(wù)一定能取消,只能盡快讓任務(wù)取消(比如,如果正在進行一些無法打斷的操作時,任務(wù)就仍然會執(zhí)行)
  • 內(nèi)存泄漏:由于它沒有與Activity等生命周期進行綁定,因此它的生命周期仍然可能比Activity長,如果將它作為Activity的非static內(nèi)部類,則它會持有Activity的引用,導致Activity的內(nèi)存無法釋放。

總結(jié)

本文基本上對AsyncTask的實現(xiàn)原理有一個大致的了解了,它的原理實際上還是挺簡單的:通過ExecutorFutureTask配合,從而實現(xiàn)任務(wù)的異步執(zhí)行,最后在任務(wù)結(jié)束后通過Handler進行線程的切換從而實現(xiàn)了整個線程調(diào)度的功能。在Android 3.0之后,默認情況下AsyncTask的執(zhí)行順序是串行進行的。


? 2020 Tyhoo Wu, All rights reserved.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容