Android Handler機制13之AsyncTask源碼解析

Android Handler機制系列文章整體內容如下:

本篇文章的主要內容如下:

  • 1、AsyncTask概述
  • 2、基本用法
  • 3、AsyncTask類源碼解析
  • 4、AsyncTask類的構造函數(shù)
  • 5、AsyncTask類核心方法解析
  • 6、AsyncTask核心流程
  • 7、AsyncTask與Handler

AsyncTask官網(wǎng)
AsyncTask源碼

一、AsyncTask概述

我們都知道,Android UI線程是不安全的,如果想要在子線程里面進行UI操作,就需要直接Android的異步消息處理機制,前面我寫了很多文章從源碼層面分析了Android異步消息Handler的處理機制。感興趣的可以去了解下。不過為了更方便我們在子線程中更新UI元素,Android1.5版本就引入了一個AsyncTask類,使用它就可以非常靈活方便地從子線程切換到UI線程。

二、基本用法

AsyncTask是一個抽象類,我們需要創(chuàng)建子類去繼承它,并且重寫一些方法。AsyncTask接受三個泛型的參數(shù):

  • Params:指定傳給任務執(zhí)行時的參數(shù)的類型
  • Progress:指定后臺任務執(zhí)行時將任務進度返回給UI線程的參數(shù)類型
  • Result:指定任務完成后返回的結果類型

除了指定泛型參數(shù),還需要根據(jù)重寫一些方法,常用的如下:

  • onPreExecute():這個方法在UI線程調用,用于在任務執(zhí)行器那做一些初始化操作,如在界面上顯示加載進度空間
  • onInBackground:在onPreExecute()結束之后立刻在后臺線程調用,用于耗時操作。在這個方法中可調用publishProgress方法返回任務的執(zhí)行進度。
  • onProgressUpdate:在doInBackground調用publishProgress后被調用,工作在UI線程
  • onPostExecute:后臺任務結束后被調用,工作在UI線程。

三、AsyncTask類源碼解析

AsyncTask.java源碼地址

(一)、類注釋翻譯

源碼注釋如下:

/**
 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
 * to perform background operations and publish results on the UI thread without
 * having to manipulate threads and/or handlers.</p>
 *
 * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
 * and does not constitute a generic threading framework. AsyncTasks should ideally be
 * used for short operations (a few seconds at the most.) If you need to keep threads
 * running for long periods of time, it is highly recommended you use the various APIs
 * provided by the <code>java.util.concurrent</code> package such as {@link Executor},
 * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
 *
 * <p>An asynchronous task is defined by a computation that runs on a background thread and
 * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
 * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
 * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
 * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
 *
 * <div class="special reference">
 * <h3>Developer Guides</h3>
 * <p>For more information about using tasks and threads, read the
 * <a href="{@docRoot}guide/components/processes-and-threads.html">Processes and
 * Threads</a> developer guide.</p>
 * </div>
 *
 * <h2>Usage</h2>
 * <p>AsyncTask must be subclassed to be used. The subclass will override at least
 * one method ({@link #doInBackground}), and most often will override a
 * second one ({@link #onPostExecute}.)</p>
 *
 * <p>Here is an example of subclassing:</p>
 * <pre class="prettyprint">
 * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
 *     protected Long doInBackground(URL... urls) {
 *         int count = urls.length;
 *         long totalSize = 0;
 *         for (int i = 0; i < count; i++) {
 *             totalSize += Downloader.downloadFile(urls[i]);
 *             publishProgress((int) ((i / (float) count) * 100));
 *             // Escape early if cancel() is called
 *             if (isCancelled()) break;
 *         }
 *         return totalSize;
 *     }
 *
 *     protected void onProgressUpdate(Integer... progress) {
 *         setProgressPercent(progress[0]);
 *     }
 *
 *     protected void onPostExecute(Long result) {
 *         showDialog("Downloaded " + result + " bytes");
 *     }
 * }
 * </pre>
 *
 * <p>Once created, a task is executed very simply:</p>
 * <pre class="prettyprint">
 * new DownloadFilesTask().execute(url1, url2, url3);
 * </pre>
 *
 * <h2>AsyncTask's generic types</h2>
 * <p>The three types used by an asynchronous task are the following:</p>
 * <ol>
 *     <li><code>Params</code>, the type of the parameters sent to the task upon
 *     execution.</li>
 *     <li><code>Progress</code>, the type of the progress units published during
 *     the background computation.</li>
 *     <li><code>Result</code>, the type of the result of the background
 *     computation.</li>
 * </ol>
 * <p>Not all types are always used by an asynchronous task. To mark a type as unused,
 * simply use the type {@link Void}:</p>
 * <pre>
 * private class MyTask extends AsyncTask<Void, Void, Void> { ... }
 * </pre>
 *
 * <h2>The 4 steps</h2>
 * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
 * <ol>
 *     <li>{@link #onPreExecute()}, invoked on the UI thread before the task
 *     is executed. This step is normally used to setup the task, for instance by
 *     showing a progress bar in the user interface.</li>
 *     <li>{@link #doInBackground}, invoked on the background thread
 *     immediately after {@link #onPreExecute()} finishes executing. This step is used
 *     to perform background computation that can take a long time. The parameters
 *     of the asynchronous task are passed to this step. The result of the computation must
 *     be returned by this step and will be passed back to the last step. This step
 *     can also use {@link #publishProgress} to publish one or more units
 *     of progress. These values are published on the UI thread, in the
 *     {@link #onProgressUpdate} step.</li>
 *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
 *     call to {@link #publishProgress}. The timing of the execution is
 *     undefined. This method is used to display any form of progress in the user
 *     interface while the background computation is still executing. For instance,
 *     it can be used to animate a progress bar or show logs in a text field.</li>
 *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
 *     computation finishes. The result of the background computation is passed to
 *     this step as a parameter.</li>
 * </ol>
 * 
 * <h2>Cancelling a task</h2>
 * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
 * this method will cause subsequent calls to {@link #isCancelled()} to return true.
 * After invoking this method, {@link #onCancelled(Object)}, instead of
 * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
 * returns. To ensure that a task is cancelled as quickly as possible, you should always
 * check the return value of {@link #isCancelled()} periodically from
 * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
 *
 * <h2>Threading rules</h2>
 * <p>There are a few threading rules that must be followed for this class to
 * work properly:</p>
 * <ul>
 *     <li>The AsyncTask class must be loaded on the UI thread. This is done
 *     automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
 *     <li>The task instance must be created on the UI thread.</li>
 *     <li>{@link #execute} must be invoked on the UI thread.</li>
 *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
 *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
 *     <li>The task can be executed only once (an exception will be thrown if
 *     a second execution is attempted.)</li>
 * </ul>
 *
 * <h2>Memory observability</h2>
 * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
 * operations are safe without explicit synchronizations.</p>
 * <ul>
 *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
 *     in {@link #doInBackground}.
 *     <li>Set member fields in {@link #doInBackground}, and refer to them in
 *     {@link #onProgressUpdate} and {@link #onPostExecute}.
 * </ul>
 *
 * <h2>Order of execution</h2>
 * <p>When first introduced, AsyncTasks were executed serially on a single background
 * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
 * to a pool of threads allowing multiple tasks to operate in parallel. Starting with
 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
 * thread to avoid common application errors caused by parallel execution.</p>
 * <p>If you truly want parallel execution, you can invoke
 * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
 * {@link #THREAD_POOL_EXECUTOR}.</p>
 */

簡單翻譯一下:

  • AsyncTask可以輕松的正確使用UI線程,該類允許你執(zhí)行后臺操作并在UI線程更新結果,從而避免了無需使用Handler來操作線程
  • AsyncTask是圍繞Thread與Handler而設計的輔助類,所以它并不是一個通用的線程框架。理想情況下,AsyncTask應該用于短操作(最多幾秒)。如果你的需求是在長時間保持線程運行,強烈建議您使用由
    java.util.concurrent提供的各種API包,比如Executor、ThreadPoolExecutor或者FutureTask。
  • 一個異步任務被定義為:在后臺線程上運行,并在UI線程更新結果。一個異步任務通常由三個類型:Params、Progress和Result。以及4個步驟:onPreExecute、doInBackground、onProgressUpdate、onPostExecute。
  • 用法:AsyncTask必須由子類實現(xiàn)后才能使用,它的子類至少重寫doInBackground()這個方法,并且通常也會重寫onPostExecute()這個方法
  • 下面是一個繼承AsyncTask類的一個子類的例子
   private class DownloadFilesTask extends AsyncTask<URL, >Integer, Long>
   {
       protected Long doInBackground(URL... urls) {
           int count = urls.length;
           long totalSize = 0;
           for (int i = 0; i < count; i++) {
               totalSize += Downloader.downloadFile(urls[i]);
               publishProgress((int) ((i / (float) count) * 100));
               // Escape early if cancel() is called
               if (isCancelled()) break;
           }
           return totalSize;
       }

       protected void onProgressUpdate(Integer... progress) {
           setProgressPercent(progress[0]);
       }

       protected void onPostExecute(Long result) {
           showDialog("Downloaded " + result + " bytes");
       }
   }

一旦創(chuàng)建,任務會被很輕松的執(zhí)行。就像下面這塊代碼一樣

new DownloadFilesTask().execute(url1,url2,url3);
  • AsyncTask泛型,在異步任務中通常有三個泛型
    • Params:發(fā)送給任務的參數(shù)類型,這個類型會被任務執(zhí)行
    • Progress:后臺線程進度的計算的基本單位。
    • Result:后臺線程執(zhí)行的結果類型。
  • 如果異步任務不需要上面類型,則可以需要聲明類型未使用,通過使用Void來表示類型未使用。就像下面一楊
private class MyTask extends AsyncTask<Void, Void, Void>{...}
  • 四個步驟
    • onPreExecute() 在執(zhí)行任務之前在UI線程上調用,此步驟通常用于初始化任務,例如在用戶界面顯示進度條。
    • doInBackground() 方法在 onPreExecute()執(zhí)行完成后調用的。doInBackground()這個方法用于執(zhí)行可能需要很長時間的首臺計算。異步任務的參數(shù)被傳遞到這個步驟中。計算結果必須經(jīng)由這個步驟返回。這個步驟內還可以調用publishProgress()方法發(fā)布一個或者多個進度單位。在調用onProgressUpdate()函數(shù)后,這個結果將在UI線程上更新。
    • onProgressUpdate()方法:在調用publishProgress()之后在UI線程上調用。具體的執(zhí)行時間不確定,該方法用于在后臺計算的異步任務,把具體的進度顯示在用戶界面。例如,它可以用于對進度條進行動畫處理或者在文本字段中顯示日志。
    • onPostExecute()方法, 后臺任務完成后,在UI線程調用onPostExecute()方法,后臺運行的結果作為參數(shù)傳遞給這個方法
  • 取消任務
    在任何時候都可以通過調用cancel(boolean)來取消任務。調用此方法將導致isCancelled()方法的后續(xù)調用返回true。調用此方法后,在執(zhí)行doInBackground(Object [])方法后,將調用onCancelled(object),而不是onPostExecute(Object)方法。為了盡可能快的取消任務,如果可能的話,你應該在調用doInBackground(Object[])之前檢查isCancelled()的返回值。
  • 線程規(guī)則,這個類必須遵循有關線程的一些規(guī)則才能正常使用,規(guī)則如下:
    • 必須在UI線程上加載AsyncTask類,android4.1上自動完成
    • 任務實例必須在UI線程上實例化
    • execute()方法必須在主線程上調用
    • 不要手動去調用onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()方法。
    • 這個任務只能執(zhí)行一次(如果嘗試第二次執(zhí)行,將會拋出異常)。
      該任務只能執(zhí)行一次(如果嘗試第二次執(zhí)行,將拋出異常)。
  • 內存的觀察AsyncTask。保證所有回調調用都是同步的,使得以下操作在沒有顯示同步情況下是安全的。
    • 在構造函數(shù)或者onPreExecute設置成員變量,并且在doInBackground()方法中引用它們。
    • 在doInBackground()設置成員字段,并在onProgressUpdate()和onPostExecute()方法中引用他們。
  • 執(zhí)行順序。第一引入AsyncTask時,AsyncTasks是在單個后臺線程串行執(zhí)行的。在android1.6以后,這被更改為允許多個任務并行操作的線程池。從android 3.0開始,每個任務都是執(zhí)行在一個獨立的線程上,這樣可以避免一些并行執(zhí)行引起的常見的應用程序錯誤。如果你想要并行執(zhí)行,可以使用THREAD_POOL_EXECUTOR來調用executeOnExecutor()方法。

至此這個類的注釋翻譯完畢,好長啊,大家看完翻譯,是不是發(fā)現(xiàn)了很多之前沒有考慮到的問題。

(二)、AsyncTask的結構

AsyncTask的結構如下:

AsyncTask的結構.png

我們看到在AsyncTask有4個自定義類,一個枚舉類,一個靜態(tài)塊,然后才是這個類的具體變量和屬性,那我們就依次講解

(三)、枚舉Status

代碼在AsyncTask.java 256行

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
   
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

枚舉Status上的注釋翻譯一下就是:

Status表示當前任務的狀態(tài),每種狀態(tài)只能在任務的生命周期內設置一次。

所以任務有三種狀態(tài)

  • PENDING:表示任務尚未執(zhí)行的狀態(tài)
  • RUNNING:表示任務正在執(zhí)行
  • FINISHED:任務已完成

(四)、私有的靜態(tài)類InternalHandler

代碼在AsyncTask.java 656行

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            // 這個handler是關聯(lián)到主線程的
            super(Looper.getMainLooper());
        }

        @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繼承自Handler,并且在它的構造函數(shù)里面調用了Looper.getMainLooper(),所以我們知道這個Handler是關聯(lián)主線程的。
  • 重寫了handleMessage(Message)方法,其中這個Message的obj這個類型是AsyncTaskResult(AsyncTaskResult我將在下面講解),然后根據(jù)msg.what的來區(qū)別。
  • 我們知道這個Message只有兩個標示,一個是MESSAGE_POST_RESULT代表消息的結果,一個是MESSAGE_POST_PROGRESS代表要執(zhí)行onProgressUpdate()方法。

通過這段代碼我們可以推測AsyncTask內部實現(xiàn)線程切換,即切換到主線程是通過Handler來實現(xiàn)的。

(五)、私有的靜態(tài)類AsyncTaskResult

代碼在AsyncTask.java 682行

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

通過類名,我們大概可以推測出這一個負責AsyncTask結果的類

AsyncTaskResult這個類 有兩個成員變量,一個是AsyncTask一個是泛型的數(shù)組。

  • mTask參數(shù):是為了AsyncTask是方便在handler的handlerMessage回調中方便調用AsyncTask的本身回調函數(shù),比如onPostExecute()函數(shù)、onPreogressUpdata()函數(shù),所以在AsyncTaskResult需要持有AsyncTask。
  • mData參數(shù):既然是代表結果,那么肯定要有一個變量持有這個計算結果

(六)、私有靜態(tài)抽象類WorkerRunnable

代碼在AsyncTask.java 677行

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

這個抽象類很簡答,首先是實現(xiàn)了Callable接口,然后里面有個變量
mParams,類型是泛型傳進來的數(shù)組

(七)、局部變量詳解

AsyncTask的局部變量如下:

    private static final String LOG_TAG = "AsyncTask";

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    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;
    private static final int KEEP_ALIVE_SECONDS = 30;

    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());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;


    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    private static InternalHandler sHandler;

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

    private volatile Status mStatus = Status.PENDING;
    
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

那我們就來一一解答

  • String LOG_TAG = "AsyncTask":打印專用
  • CPU_COUNT = Runtime.getRuntime().availableProcessors():獲取當前CPU的核心數(shù)
  • CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)):線程池的核心容量,通過代碼我們知道是一個大于等于2小于等于4的數(shù)
  • MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1:線程池的最大容量是2倍的CPU核心數(shù)+1
  • KEEP_ALIVE_SECONDS = 30:過剩的空閑線程的存活時間,一般是30秒
  • sThreadFactory:線程工廠,通過new Thread來獲取新線程,里面通過使用AtomicInteger原子整數(shù)保證超高并發(fā)下可以正常工作。
  • sPoolWorkQueue:靜態(tài)阻塞式隊列,用來存放待執(zhí)行的任務,初始容量:128個
  • THREAD_POOL_EXECUTOR:線程池
  • SERIAL_EXECUTOR = new SerialExecutor():靜態(tài)串行任務執(zhí)行器,其內部實現(xiàn)了串行控制,循環(huán)的取出一個個任務交給上述的并發(fā)線程池去執(zhí)行。
  • MESSAGE_POST_RESULT = 0x1:消息類型,代表發(fā)送結果
  • MESSAGE_POST_PROGRESS = 0x2:消息類型,代表進度
  • sDefaultExecutor = SERIAL_EXECUTOR:默認任務執(zhí)行器,被賦值為串行任務執(zhí)行器,就是因為它,AsyncTask變成了串行的了。
  • sHandler:靜態(tài)Handler,用來發(fā)送上面的兩種通知,采用UI線程的Looper來處理消息,這就是為什么AnsyncTask可以在UI線程更新UI
  • WorkerRunnable<Params, Result> mWorke:是一個實現(xiàn)Callback的抽象類,擴展了Callable多了一個Params參數(shù)。
  • mFuture:FutureTask對象
  • mStatus = Status.PENDING:任務的狀態(tài)默認為掛起,即等待執(zhí)行,其類型標示為volatile
  • mCancelled = new AtomicBoolean():原子布爾類型,支持高并發(fā)訪問,標示任務是否被取消
  • mTaskInvoked = new AtomicBoolean():原子布爾類型,支持高并發(fā)訪問,標示任務是否被執(zhí)行過

(八)、靜態(tài)代碼塊

代碼在AsyncTask.java 226行

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

通過上面的(七)、局部變量詳解,我們知道在靜態(tài)代碼塊中創(chuàng)建了一個線程池threadPoolExecutor,并設置了核心線程會超時關閉,最后并把這個線程池指向THREAD_POOL_EXECUTOR。

(九)、私有的靜態(tài)類SerialExecutor

代碼在AsyncTask.java 226行

    private static class SerialExecutor implements Executor {
        // 循環(huán)數(shù)組實現(xiàn)的雙向Queue,大小是2的倍數(shù),默認是16,有隊頭和隊尾巴兩個下標
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        // 正在運行runnable
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            // 添加到雙向隊列中去
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                         //執(zhí)行run方法
                        r.run();
                    } finally {
                        //無論執(zhí)行結果如何都會取出下一個任務執(zhí)行
                        scheduleNext();
                    }
                }
            });
           // 如果沒有活動的runnable,則從雙端隊列里面取出一個runnable放到線程池中運行
           // 第一個請求任務過來的時候mActive是空的
            if (mActive == null) {
                 //取出下一個任務來
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //從雙端隊列中取出一個任務
            if ((mActive = mTasks.poll()) != null) {
                //線線程池執(zhí)行取出來的任務,真正的執(zhí)行任務
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
  • 首先,注意SerialExecutor的execute是synchronized的,所以無論多個少任務調用execute()都是同步的。
  • 其次,SerialExecutor里面一個ArrayDeque隊列,通過代碼我們知道,SerialExecutor是通過ArrayDeque來管理Runnable對象的。通過上面我們知道execute()是同步的,所以如果你有10個任務同時調用SerialExecutor的execute()方法,就會把10個Runnable先后放入到mTasks中去,可見mTasks緩存了將要執(zhí)行的Runnable。
  • 再次1,如果我們第一個執(zhí)行execute()方法時,會調用ArrayDeque的offer()方法將傳入的Runnable對象添加到隊列的尾部,然后判斷mActive是不是null,因為是第一次調用,此時mActive還沒有賦值,所以mActive等于null。所以此時mActive == null成立,所以會調用scheduleNext()方法。
  • 再次2,我們在調用scheduleNext()里面,看到會調用mTasks.poll(),我們知道這是從隊列中取出頭部元素,然后把這個頭部元素賦值給mActive,然后讓THREAD_POOL_EXECUTOR這個線程池去執(zhí)行這個mActive的Runnable對象。
  • 再次3,如果這時候有第二個任務入隊,但是此時mActive!=null,不會執(zhí)行scheduleNext(),所以如果第一個任務比較耗時,后面的任務都會進入隊列等待。
  • 再次4,上面知道由于第二個任務入隊后,由于mActive!=null,所以不會執(zhí)行scheduleNext(),那樣這樣后面的任務豈不是永遠得不到處理,當然不是,因為在offer()方法里面?zhèn)魅胍粋€Runnable的匿名類,并且在此使用了finnally代碼,意味著無論發(fā)生什么情況,這個finnally里面的代碼一定會執(zhí)行,而finnally代碼塊里面就是調用了scheduleNext()方法,所以說每當一個任務執(zhí)行完畢后,下一個任務才會執(zhí)行。
  • 最后,SerialExecutor其實模仿的是單一線程池的效果,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執(zhí)行,其余的均處于等待狀態(tài)。

PS:scheduleNext()方法是synchronized,所以也是同步的

重點補充:

在Android 3.0 之前是并沒有SerialExecutor這個類的,那個時候是直接在AsyncTask中構建一個sExecutor常量,并對線程池總大小,同一時刻能夠運行的線程數(shù)做了規(guī)定,代碼如下:

private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
        MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); 

四、AsyncTask類的構造函數(shù)

代碼如下:

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {

                // 設置方法已經(jīng)被調用
                mTaskInvoked.set(true);
                 // 設定結果變量
                Result result = null;
                try {
                    //設置線程優(yōu)先級
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    //執(zhí)行任務
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    // 產(chǎn)生異常則設置失敗
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 無論執(zhí)行成功還是出現(xiàn)異常,最后都會調用PostResult
                    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);
                }
            }
        };
    }

通過注釋我們知道,這個方法創(chuàng)建一個異步任務,構造函數(shù)必須在UI線程調用

這里面設計了兩個概念Callable和FutureTask,如果大家對這兩個類有疑問,可以看我上一篇文章Android Handler機制12之Callable、Future和FutureTask

構造函數(shù)也比較簡單,主要就是給mWorker和mFuture初始化,其中WorkerRunnable實現(xiàn)了Callable接口,

在構造函數(shù)里面調用了postResult(Result)和postResultIfNotInvoked(Result),那我們就來分別看下

1、postResult(Result)方法
     // doInBackground執(zhí)行完畢,發(fā)送消息
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        // 獲取一個Message對象
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        // 發(fā)送給線程
        message.sendToTarget();
        return result;
    }

通過代碼我們知道

  • 生成一個Message
  • 把這個Message發(fā)送出

這里面調用了 getHandler(),那我們來看下這個方法是怎么寫的

2、getHandler()方法
    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

我們看到返回的是InternalHandler對象,上面說過了InternalHandler其實是關聯(lián)主線程的,所以上面方法 message.sendToTarget(); 其實是把消息發(fā)送給主線程。

大家注意一下 這里的Message的what值為MESSAGE_POST_RESULT,我們來看下InternalHandler遇到InternalHandler這種消息是怎么處理的

    private static class InternalHandler extends Handler {
        ... 
        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;
               ...
            }
        }
    }

我們看到MESSAGE_POST_RESULT對應的是指是執(zhí)行AsyncTask的finish(Result)方法,所以我們可以這樣說,無論AsyncTask是成功了還是失敗了,最后都會執(zhí)行finish(Result)方法。那我們來看下finish(Result)方法里面都干了什么?

2.1、finish(Result result) 方法
    private void finish(Result result) {
        if (isCancelled()) {
            // 如果消息取消了,執(zhí)行onCancelled方法
            onCancelled(result);
        } else {
            // 如果消息沒有取消,則執(zhí)行onPostExecute方法
            onPostExecute(result);
        }
        // 設置狀態(tài)值
        mStatus = Status.FINISHED;
    }

注釋寫的很清楚了,我這里就不說明了,通過上面的代碼和finish方法的分析,我們知道無論成功還是失敗,最后一定會調用finish(Result)方法,所以最后狀態(tài)的值為FINISHED。

3、postResultIfNotInvoked(Result)方法
    private void postResultIfNotInvoked(Result result) {
        // 獲取mTaskInvoked的值
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

通過上面代碼我們知道,如果mTaskInvoked不為true,則執(zhí)行postResult,但是在mWorker初始化的時候為true,除非在沒有執(zhí)行call方法時候,如果沒有執(zhí)行call,說明這個異步線程還沒有開始執(zhí)行,這個時候mTaskInvoked為false。而這時候調用postResultIfNotInvoked則還是會執(zhí)行postResult(Result),這樣保證了AsyncTask一定有返回值。

五、AsyncTask類核心方法解析

(一)、void onPreExecute()

    /**
     * Runs on the UI thread before {@link #doInBackground}.
     *
     * @see #onPostExecute
     * @see #doInBackground
     */
     // 在調用doInBackground()方法之前,跑在主線程上
    @MainThread
    protected void onPreExecute() {
    }

其實注釋很清楚了,在task任務開始執(zhí)行的時候在主線程調用,在doInBackground(Params… params) 方法之前調用。

(二)、AsyncTask<Params, Progress, Result> onPreExecute() 方法

    /**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * <p>Note: this function schedules the task on a queue for a single background
     * thread or pool of threads depending on the platform version.  When first
     * introduced, AsyncTasks were executed serially on a single background thread.
     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
     * to a pool of threads allowing multiple tasks to operate in parallel. Starting
     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
     * executed on a single thread to avoid common application errors caused
     * by parallel execution.  If you truly want parallel execution, you can use
     * the {@link #executeOnExecutor} version of this method
     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
     * on its use.
     *
     * <p>This method must be invoked on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     *
     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
     * @see #execute(Runnable)
     */
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

首先來翻譯一下注釋

  • 使用指定的參數(shù)來執(zhí)行任務,這個方法的返回值是this,也就是它自己,因為這樣設計的目的是可以保持對它的引用。
  • 注意:它的調度模式是不同的,一種是單個后臺線程,一種是通過線程池來實現(xiàn),具體那種模式是根據(jù)android版本的不同而不同,當最開始引入AsyncTask的時候,AsyncTask是單個后臺線程上串行執(zhí)行,從Android DONUT 開始,模式變更為通過線程池多任務并行執(zhí)行。在Android HONEYCOMB開始,又變回了在單個線程上執(zhí)行,這樣可以避免并行執(zhí)行的錯誤。如果你還是想并行執(zhí)行,你可以使用executeOnExecutor()方法并且第一個參數(shù)是THREAD_POOL_EXECUTOR就可以了,不過,請注意有關使用警告。
  • 必須在UI主線程上調用此方法。

通過代碼我們看到,它的內部其實是調用executeOnExecutor(Executor exec, Params... params)方法,只不過第一個參數(shù)傳入的是sDefaultExecutor,而sDefaultExecutor是SerialExecutor的對象。上面我們提到了SerialExecutor里面利用ArrayDeque來實現(xiàn)串行的,所以我們可以推測出如果在executeOnExecutor(Executor exec, Params... params)方法里面如果第一個參數(shù)是自定義的Executor,AsyncTask就可以實現(xiàn)并發(fā)執(zhí)行。

(三)、executeOnExecutor(Executor exec, Params... params) 方法

    /**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
     * allow multiple tasks to run in parallel on a pool of threads managed by
     * AsyncTask, however you can also use your own {@link Executor} for custom
     * behavior.
     * 
     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
     * a thread pool is generally <em>not</em> what one wants, because the order
     * of their operation is not defined.  For example, if these tasks are used
     * to modify any state in common (such as writing a file due to a button click),
     * there are no guarantees on the order of the modifications.
     * Without careful work it is possible in rare cases for the newer version
     * of the data to be over-written by an older one, leading to obscure data
     * loss and stability issues.  Such changes are best
     * executed in serial; to guarantee such work is serialized regardless of
     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
     *
     * <p>This method must be invoked on the UI thread.
     *
     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
     *              convenient process-wide thread pool for tasks that are loosely coupled.
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     *
     * @see #execute(Object[])
     */
    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        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)");
            }
        }
        //設置狀態(tài)
        mStatus = Status.RUNNING;
        從這里我們看出onPreExecute是先執(zhí)行,并且在UI線程
        onPreExecute();
        // 設置參數(shù)
        mWorker.mParams = params;
        // 開啟了后臺線程去計算,這是真正調用doInBackground的地方
        exec.execute(mFuture);
        // 接著會有onProgressUpdate會被調用,最后是onPostExecute
        return this;
    }

老規(guī)矩 先翻譯一下注釋:

  • 使用指定的參數(shù)來執(zhí)行任務,這個方法的返回值是this,也就是它自己,因為這樣設計的目的是可以保持對它的引用。
  • 這個方法通常與THREAD_POOL_EXECUTOR一起使用,這樣可以讓多個人物在AsyncTask管理的線程池上并行運行,但你也可以使用自定義的Executor。
  • 警告:由于大多數(shù)情況并沒有定義任務的操作順序,所以在線程池中多任務并行并不常見。例如:如果修改共同狀態(tài)的任務(就像點擊按鈕就可以編寫文件),對修改的順訊沒有保證。在很少的情況下,如果沒有仔細工作,較新版本的數(shù)據(jù)可能會被較舊的數(shù)據(jù)覆蓋,從而導致數(shù)據(jù)丟失和穩(wěn)定性問題。而這些變更最好是連續(xù)執(zhí)行,因為這樣可以保證工作的有序化,無論平臺版本如何,你可以使用SERIAL_EXECUTOR。
  • 必須在UI主線程上調用此方法。
  • 參數(shù)exec:為了實現(xiàn)輕松解耦,我們可以使用THREAD_POOL_EXECUTOR這個線程池可以作為合適的進程范圍的線程池
  • 參數(shù)params:任務的參數(shù)

那我們來看下一下代碼,代碼里面的邏輯如下:

  • 該方法首先是判斷mStatus狀態(tài),如果是正在運行(RUNNING)或者已經(jīng)結束(FINISHED),就會拋出異常。
  • 接著設置狀態(tài)為RUNNING,即運行,執(zhí)行onPreExecute()方法,并把參數(shù)的值賦給mWorker.mParams
  • 于是Executor去執(zhí)行execute的方法,學過Java多線程的都知道,這里方法是開啟一個線程去執(zhí)行mFuture的run()方法(由于mFuture用Callable構造,所以其實是執(zhí)行的Callable的call()方法,而mWorker是Callable的是實現(xiàn)類,所以最終執(zhí)行的是mWorker的call()方法)

PS:mFuture和mWorker都是在AsyncTask的構造方法中初始化過的。

(四)、 publishProgress(Progress... values) 方法

主要是設置后臺進度,onProgressUpdate會被調用

    /**
     * This method can be invoked from {@link #doInBackground} to
     * publish updates on the UI thread while the background computation is
     * still running. Each call to this method will trigger the execution of
     * {@link #onProgressUpdate} on the UI thread.
     *
     * {@link #onProgressUpdate} will not be called if the task has been
     * canceled.
     *
     * @param values The progress values to update the UI with.
     *
     * @see #onProgressUpdate
     * @see #doInBackground
     */
    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

這個方法內部實現(xiàn)很簡單

  • 首先判斷 任務是否已經(jīng)被取消,如果已經(jīng)被取消了,則什么也不做
  • 如果任務沒有被取消,則通過InternalHandler發(fā)送一個what為MESSAGE_POST_PROGRESS的Message

這樣就進入了InternalHandler的handleMessage(Message)里面了,而我們知道InternalHandler的Looper是Looper.getMainLooper(),所以處理Message是在主線程中,我們來看下代碼

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @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;
            }
        }
    }

通過代碼,我們看到如果what為MESSAGE_POST_PROGRESS,則會在主線程中調用onProgressUpdate(result.mData),這也就是為什么我們平時在異步線程調用publishProgress(Progress...)方法后,可以在主線程中的onProgressUpdate(rogress... values)接受數(shù)據(jù)了。

六、AsyncTask核心流程

其實在上面講解過程中,我基本上已經(jīng)把整體流程講解過了,我這里補上一張圖,比較全面的闡述了AsyncTask的執(zhí)行流程如下:

asynctask執(zhí)行流程.png

對應的時序圖如下:

時序圖.png

大家如果手機上看不清,我建議down下來在電腦上看。

如果結合AsyncTask的狀態(tài)值,流程圖如下:

流程.png

如果把AsyncTask和Handler分開則流程圖如下:

AsyncTask和Handler分開.png

最后如果把AsyncTask里面所有類的涉及關系整理如下圖:

20140513095959437.jpeg

七、AsyncTask與Handler

  • AsyncTask:
    • 優(yōu)點:AsyncTask是一個輕量級的異步任務處理類,輕量級體現(xiàn)在,使用方便,代碼簡潔,而且整個異步任務的過程可以通過cancel()進行控制
    • 缺點:不適用處理長時間的異步任務,一般這個異步任務的過程最好控制在幾秒以內,如果是長時間的異步任務就需要考慮多線程的控制問題;當處理多個異步任務時,UI更新變得困難。
  • Handler:
    • 優(yōu)點:代碼結構清晰,容易處理多個異步任務
    • 缺點:當有多個異步任務時,由于要配合Thread或Runnable,代碼可能會稍顯冗余。

總之,AsyncTask不失為一個非常好用的異步任務處理類。不過我從事Android開發(fā)5年多了,很少會用到AsyncTask,一般異步任務都是Handler。

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容