Android 源碼分析之 AsyncTask 源碼分析

一、AsyncTask的使用

使用 AsyncTask 能夠更加簡單地實現任務的異步執行,以及任務執行完畢以後與主線程的交互。它被設計用來執行耗時比較短的任務,一般是幾秒種的那種,若是要執行耗時比較長的任務,那麼就應該使用 JUC 包中的框架,好比 ThreadPoolExecutorFutureTask等。java

AsyncTask 用來在後臺線程中執行任務,當任務執行完畢以後將結果發送到主線程當中。它有三個重要的泛類型參數,分別是 ParamsProgressResult,分別用來指定參數、進度和結果的值的類型。 以及四個重要的方法,分別是 onPreExecute(), doInBackground(), onProgressUpdate()onPostExecute()。 這四個方法中,除了 doInBackground(),其餘三個都是運行在UI線程的,分別用來處理在任務開始以前、任務進度改變的時候以及任務執行完畢以後的邏輯,而 doInBackground() 運行在後臺線程中,用來執行耗時的任務。android

一種典型的使用方法以下:git

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    
    @Override
    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));
            if (isCancelled()) break;
        }
        return totalSize;
    }

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

    @Override
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}
複製代碼

上面說 AsyncTask 有4個重要的方法,這裏咱們覆寫了3個。doInBackground() 運行在線程當中,耗時的任務能夠放在這裏進行;onProgressUpdate() 用來處理當任務的進度發生變化時候的邏輯;onPostExecute() 用來處理當任務執行完畢以後的邏輯。另外,這裏咱們還用到了 publishProgress()isCancelled() 兩個方法,分別用來發布任務進度和判斷任務是否被取消。github

而後,咱們能夠用下面的方式來使用它:框架

new DownloadFilesTask().execute(url1, url2, url3);
複製代碼

使用AsyncTask的時候要注意如下幾點內容:異步

  1. AsyncTask 的類必須在主線程中進行加載,當在4.1以後這個過程會自動進行;
  2. AsyncTask 的對象必須在主線程中建立;
  3. execute() 方法必須在UI線程中被調用;
  4. 不要直接調用 onPreExecute(), doInBackground(), onProgressUpdate()onPostExecute()
  5. 一個AsyncTask對象的 execute() 方法只能被調用一次;

Android 1.6 以前,AsyncTask 是串行執行任務的;1.6 採用線程池處理並行任務;從 3.0 開始,又採用一個線程來串行執行任務。 3.0 以後能夠用 executeOnExecutor() 來並行地執行任務,若是咱們但願在3.0以後能並行地執行上面的任務,那麼咱們應該這樣去寫:ide

new DownloadFilesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2, url3);
複製代碼

這裏的 AsyncTask.THREAD_POOL_EXECUTOR 是 AsyncTask 內部定義的一個線程池,咱們可使用它來將 AsyncTask 設置成並行的。oop

二、AsyncTask源碼分析

2.1 AsyncTask 的初始化過程

當初始化一個 AsyncTask 的時候,全部的重載構造方法都會調用下面的這個構造方法。這裏作了幾件事情:源碼分析

  1. 初始化一個 Handler 對象 mHandler,該 Handler 用來將消息發送到它所在的線程中,一般使用默認的值,即主線程的 Handler;
  2. 初始化一個 WorkerRunnable 對象 mWorker。它是一個 WorkerRunnable 類型的實例,而 WorkerRunnable 又繼承自 Callable,所以它是一個能夠被執行的對象。咱們會把在該對象中回調 doInBackground() 來將咱們的業務邏輯放在線程池中執行。
  3. 初始化一個 FutureTask 對象 mFuture。該對象包裝了 mWorker 而且當 mWorker 執行完畢以後會調用它的 postResultIfNotInvoked() 方法來通知主線程(不論任務已經執行完畢仍是被取消了,都會調用這個方法)。
public AsyncTask(@Nullable Looper callbackLooper) {
        // 1. 初始化用來發送消息的 Handler
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        // 2. 封裝一個對象用來執行咱們的任務
        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);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 發送結果給主線程
                    postResult(result);
                }
                return result;
            }
        };

        // 3. 初始化一個 FutureTask,而且當它執行完畢的時候,會調用 postResultIfNotInvoked 來將消息的執行結果發送到主線程中
        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);
                }
            }
        };
    }
複製代碼

當這樣設置完畢以後,咱們就可使用 execute() 方法來開始執行任務了。post

2.2 AsyncTask 中任務的串行執行過程

咱們從 execute() 方法開始分析 AsyncTask,

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

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
        if (mStatus != Status.PENDING) { // 1.判斷線程當前的狀態
            switch (mStatus) {
                case RUNNING: throw new IllegalStateException(...);
                case FINISHED: throw new IllegalStateException(...);
            }
        }
        mStatus = Status.RUNNING;
        onPreExecute();             // 2.回調生命週期方法
        mWorker.mParams = params;   // 3.賦值給可執行的對象 WorkerRunnable
        exec.execute(mFuture);      // 4.在線程池中執行任務
        return this;
    }
複製代碼

當咱們調用 AsyncTask 的 execute() 方法的時候會當即調用它的 executeOnExecutor() 方法。這裏傳入了兩個參數,分別是一個 Executor 和任務的參數 params。從上面咱們能夠看出,當直接調用 execute() 方法的時候會使用默認的線程池 sDefaultExecutor,而當咱們指定了線程池以後,會使用咱們指定的線程池來執行任務。

在 1 處,會對 AsyncTask 當前的狀態進行判斷,這就對應了前面說的,一個任務只能被執行一次。在 2 處會調用 onPreExecute() 方法,若是咱們覆寫了該方法,那麼它就會在這個時候被調用。在 3 處的操做是在爲 mWorker 賦值,即把調用 execute 方法時傳入的參數賦值給了 mWorker。接下來,會將 mFuture 添加到線程池中執行。

當咱們不指定任何線程池的時候使用的 sDefaultExecutor 是一個串行的線程池,它的定義以下:

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) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        // 至關於對傳入的Runnable進行了一層包裝
                        r.run();
                    } finally {
                        // 分配下一個任務
                        scheduleNext();
                    }
                }
            });
            // 若是當前沒有正在執行的任務,那麼就嘗試從隊列中取出並執行
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            // 從隊列中取任務並使用THREAD_POOL_EXECUTOR執行
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
複製代碼

從上面咱們能夠看出,咱們添加到線程池中的任務實際上並無直接交給線程池來執行,而是對其進行了處理以後才執行的,SerialExecutor 經過內部維護了雙端隊列,每當一個 AsyncTask 調用 execute() 方法的時候都會被放在該隊列當中進行排隊。若是當前沒有正在執行的任務,那麼就從隊列中取一個任務交給 THREAD_POOL_EXECUTOR 執行;當一個任務執行完畢以後又會調用 scheduleNext() 取下一個任務執行。也就是說,實際上 sDefaultExecutor 在這裏只是起了一個任務調度的做用,任務最終仍是交給 THREAD_POOL_EXECUTOR 執行的。

這裏的THREAD_POOL_EXECUTOR也是一個線程池,它在靜態代碼塊中被初始化:

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;
    }
複製代碼

咱們也能夠直接將這個靜態的線程池做爲咱們任務執行的線程池而不是放在上面的隊列中被串行地執行。

2.3 將任務執行的結果發送到其餘線程

上面的 WorkerRunnable 中已經用到了 postResult 方法,它用來將任務執行的結果發送給 Handler

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

mHandler 會在建立 AsyncTask 的時候初始化。咱們能夠經過 AsyncTask 的構造方法傳入 Handler 和 Looper 來指定該對象所在的線程。當咱們沒有指定的時候,會使用 AsyncTask 內部的 InternalHandler 建立 Handler

private final Handler mHandler;

    public AsyncTask(@Nullable Looper callbackLooper) {
        // 根據傳入的參數建立Handler對象
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() 
            ? getMainHandler() : new Handler(callbackLooper);
    }

    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                // 使用 InternalHandler 建立對象
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

    // AsyncTask 內部定義 的Handler 類型
    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @Override public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            // 根據傳入的消息類型進行處理
            switch (msg.what) {
                case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break;
                case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break;
            }
        }
    }
複製代碼

三、總結

上面咱們梳理了 AsyncTask 的大體過程,咱們來梳理下:

每當咱們實例化一個 AsyncTask 的時候都會在內部封裝成一個 Runnable 對象,該對象能夠直接放在線程池中執行。這裏存在兩個線程池,一個是 SerialExecutor 一個是 THREAD_POOL_EXECUTOR,前者主要用來進行任務調度,即把交給線程的任務放在隊列中進行排隊執行,而時機上全部的任務都是在後者中執行完成的。這個兩個線程池都是靜態的字段,因此它們對應於整個類的。也就是說,當使用默認的線程池的時候,實例化的 AsyncTask 會一個個地,按照加入到隊列中的順序依次執行。

當任務執行完畢以後,使用 Handler 來將消息發送到主線程便可,這部分的邏輯主要與 Handler 機制相關,能夠經過這篇文章來了解:《Android 消息機制:Handler、MessageQueue 和 Looper》

以上就是 AsyncTask 的主要內容。


若是您喜歡個人文章,能夠在如下平臺關注我:

相關文章
相關標籤/搜索