1. 程式人生 > >帶你輕鬆看原始碼---AsyncTask(非同步任務)

帶你輕鬆看原始碼---AsyncTask(非同步任務)

本文出自部落格Vander丶CSDN部落格,如需轉載請標明出處,尊重原創謝謝

寫作背景

  • 愚人節特別篇

  • 這篇部落格準備了好久,一直放在草稿箱裡面,隨著之前的深入瞭解Android執行緒池 . 完結之後,在寫這篇文章就順手了很多.

  • 寫這篇部落格的時候,只想瞭解下AsyncTask,沒想到看著原始碼之後就沉迷了,一點一點看了佇列,同步,非同步,也嘗試著翻譯英文文件,總之收貨滿滿.

  • 本文用圖,全部為本人獨立創作,如果轉載請註明出處,請尊重原創謝謝.

  • 本文大部分觀點,屬於博主個人觀點,如有錯誤,希望大家多多指正,然後博主及時更改.

  • 最後希望看到博主文章的小夥伴,能夠給博主頂一頂部落格,歡迎留言,歡迎討論.

前言:

  • 本篇文章主要介紹AsyncTask的內部工作原理

  • 在閱讀文字之前,如果你不太瞭解AsyncTask,可以先參考我的部落格AsyncTask(非同步任務)分析之基本使用

  • 文中涉及到一些術語如:序列、並行、佇列這些關鍵詞會在導讀中為大家介紹

導讀:

AsyncTask的組成很簡單,但是還有有一些關鍵的知識點,導讀中將會把一些比較重要的概念整理出來,讓大家提前瞭解下,會使接下來的閱讀更順暢。

序列和並行的概念

在Android3.0之前,AsyncTask一直是並行執行的,而在Android3.0之後更改為序列執行,如果對序列和並行概念不太瞭解,可以參閱下我之前的

執行緒中同步、非同步、序列、並行這篇文章,通過圖能讓你迅速瞭解序列和並行的基本概念。

Android的訊息機制,Handler的原理

在AsyncTask的工作原理中,InternalHandler是其運轉的主要一環,AsyncTask通過它,來保持和UI執行緒的通訊,從而實現進度的更新,以及AsyncTask的回撥、中斷的通知。
如果對Handler機制不太熟悉,可以參考下android的訊息機制——Handler機制這篇文章。

佇列的概念,容器ArrayDeque的使用

在AsyncTask中,有一個雙端佇列的執行緒池,通過其來保持AsyncTask的排程,而其內部的容器就是ArrayDeque,對於ArrayDeque網上的介紹比較少,這裡可以參考我整理的

深入瞭解雙端佇列Deque能夠大致瞭解ArrayDeque的結構。

Android中的執行緒池

AsyncTask中採用執行緒池用於任務的排程和執行,如果對執行緒池不太瞭解,可以參考我的博文深入瞭解Android執行緒池 .

AsyncTask的原始碼解析

AsyncTask的主要成員

從AsyncTask的內部模組圖可以看出,AsyncTask主要分為以下幾個模組 :

(1)用於任務的排程的執行緒池SerialExecutor,圖中的任務佇列池.
(2)用於任務的執行的執行緒池ThreadPoolExecutor,圖中的執行任務的執行緒池.
(3)用於和UI執行緒互動的InternalHandler,通知任務的完成的進度,以及任務的一些狀態.

AsyncTask的狀態

    public enum Status {
        /**
         * AsyncTask的初始狀態,表明AsyncTask處於未執行任務的狀態
         */
        PENDING,
        /**
         * AsyncTask的執行狀態,表明AsyncTask正在執行任務,正在執行
         */
        RUNNING,
        /**
         * AsycnTask的完成狀態,表明AsyncTask處於完成狀態,並且會呼叫onPostExecute
         */
        FINISHED,
    }

任務排程執行緒池SerialExecutor

我們能夠看到SerialExecutor只是實現了Executor這個介面,其內部維持的佇列是ArrayDeque(一種雙向佇列).它會在一個任務執行結束,和SerialExecutor初始化之後,會自動去佇列中獲取任務並通過THREAD_POOL_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 {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //如果當前沒有任務執行,就去佇列中取出一個執行
            if (mActive == null) {
                scheduleNext();
            }
        }

        //從佇列中取出元素並執行的方法.
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

任務的執行的執行緒池THREAD_POOL_EXECUTOR

執行執行緒池的初始化過程:

    //獲得當前CPU的核心數
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //設定執行緒池的核心執行緒數2-4之間,但是取決於CPU核數
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    //設定執行緒池的最大執行緒數為 CPU核數*2+1
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    //設定執行緒池空閒執行緒存活時間30s
    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());
        }
    };

    //初始化儲存任務的佇列為LinkedBlockingQueue 最大容量為128
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * 一個可以執行並行任務的執行緒池哦
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        //設定核心執行緒池的 超時時間也為30s
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

用於和UI互動的InternalHandler

    private static class InternalHandler extends Handler {

        //注意:這裡是獲取主執行緒的Looper(),也就是為什麼AsyncTask能和主執行緒互動的原因
        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:
                    //這裡將結果通過Handler傳遞到主執行緒
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //這是是通知主執行緒更新進度的操作.
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

AsyncTask的內部模組圖

AsyncTask的內部模組圖

上圖是AsyncTask的內部模組圖,通過此圖大家能夠了解AsyncTask簡單的工作原理。

AsyncTask的構造方法

public AsyncTask() {
        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);
                    //將程序中未執行的命令,一併送往CPU處理
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    //如果執行異常,設定取消的標誌
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //傳送結果
                    postResult(result);
                }
                return result;
            }
        };

        //一個包裝任務的包裝類
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    //在執行完任務做一道檢查,將沒被呼叫的Result也一併發出.
                    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) {
                    //如果發生異常,則將結果滯null發出.
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

在AsyncTask的構造方法中,將任務包裝好,然後進行初始化操作:

在構造方法中,WorkerRunnable就一個能儲存引數的Callable.

//這裡的Callable也是任務,但是與Runnable不同的是,Callable<T>存在返回值,返回值就為其泛型
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

而FutureTask也是一個包裝類,我們來看下FutureTask的構造方法:

//其實FutureTask內部包含Callable<T>,並且增加了一些狀態標識和暴漏出操作Callable<T>的一些介面.
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;      
    }

而其done()方法,就是FutureTask內的Callable執行完成之後的呼叫方法.在done()方法中,對任務的呼叫進行復查,將未被呼叫的任務的結果通過InternalHandler傳遞到UI執行緒.

//方法很簡單,就是取得標誌,然後判斷該標誌而已,複查沒有被呼叫的任務,將其Result物件傳送出去.
    private void postResultIfNotInvoked()(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        //如果沒有被執行,也需要把結果傳送出去.
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

AsyncTask的工作原理

通過上面的介紹,我們瞭解了AsyncTask的核心組成,以及AsyncTask在初始化所做的操作.

execute()

那麼接下來,我們來一起看下AsyncTask的工作原理,這裡我們從AsyncTask的執行方法execute()開始:

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //從這裡我們發現最終呼叫的方法是executeOnExecutor()方法
    //此時是通過佇列執行緒池儲存任務,然後執行執行緒池取出任務執行.
        return executeOnExecutor(sDefaultExecutor, params);
    }

executeOnExecutor()

   @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
            //判斷AsyncTask當前的執行狀態,PENDING為初始化狀態
        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)");
            }
        }
        //executeOnExecutor()被呼叫時,AsyncTask的狀態就變成了RUNNING狀態
        mStatus = Status.RUNNING;
        //此時進行準備工作(主執行緒)
        onPreExecute();
        //將引數新增到任務中
        mWorker.mParams = params;
        //執行任務
        exec.execute(mFuture);
        return this;
    }

AsyncTask的基本執行流程大致的情況,上面已經介紹,需要注意的是,在Android3.0之前AsyncTask是並行執行的.

而在Android3.0之後預設AsyncTask是序列執行的.關於執行緒的序列和並行這裡如果不明白,可以參考導讀.具體大家可以在Android模擬器分別在3.0以下和3.0以上的版本進行測試.

而如果在Android3.0以上的版本並行執行AsyncTask,我們可以這樣:

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.d(TAG, "非同步任務完成階段階段");
        SimpleDateFormat df =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Log.d(TAG, "當前非同步任務 : "+"\t"+ this.hashCode()+"" );
        Log.d(TAG, "當前結束時間 : "+"\t"+df.format(new Date()) );
    }
//這裡直接呼叫executeOnExecutor()方法,並且制定處理的執行緒池為 //AsyncTask.THREAD_POOL_EXECUTOR,也就是執行執行緒池
new 
TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

通過在完成方法列印當前的AsyncTask的hashCode來區分是不是同一個AsyncTask,以及時間上的差異來確定是否是並行,得到的結果為:

03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前非同步任務 :  632327564
03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前結束時間 :  2017-03-31 13:13:48
03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 非同步任務完成階段階段
03-31 13:13:48.506 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前非同步任務 :  496311509
03-31 13:13:48.506 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前結束時間 :  2017-03-31 13:13:48
03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 非同步任務完成階段階段
03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前非同步任務 :  688151786
03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前結束時間 :  2017-03-31 13:13:48
03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 非同步任務完成階段階段
03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前非同步任務 :  304548827
03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 當前結束時間 :  2017-03-31 13:13:48

由上述結果可以發現,上述的並行的執行過程直接繞過了佇列執行緒池,直接制定執行執行緒池去執行任務.那麼並行的原因在哪呢 ?

public AsyncTask() {
                    ......
                try {
                    //設定執行緒的優先順序
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //執行非同步操作
                    result = doInBackground(mParams);
                    //將程序中未執行的命令,一併送往CPU處理
                    Binder.flushPendingCommands();
                    .........

注意Binder.flushPendingCommands()這個JNI命令.

    /**
     * Flush any Binder commands pending in the current thread to the kernel
     * driver.  This can be
     * useful to call before performing an operation that may block for a long
     * time, to ensure that any pending object references have been released
     * in order to prevent the process from holding on to objects longer than
     * it needs to.
     */
    public static final native void flushPendingCommands();

這個方法我的理解是將程序中等待的Binder命令,一併提交給CPU處理,當然這會造成一些阻塞,我們知道執行緒的執行也是依靠CPU來運轉,所以我認為這個方法才是AsyncTask能夠同步執行的關鍵.

在每個任務執行完時,都會把當前程序的等待任務提交,然後阻塞,等都完成,該程序內沒有Thread提交時,一起返回,從而形成同步.

而在這裡之所以不去使用佇列執行緒池的原因也在這,因為佇列執行緒池實現了鎖的機制,並且通過它的程式碼我們可以得知它是處理完一個任務,才會去下個任務,這一塊也是AsyncTask預設能夠實現序列的原因.

這裡寫圖片描述

postResult()方法

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        //正常的把結果通過InternalHandler傳送給UI執行緒.
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

這個方法,沒有太多可介紹的.熟悉Handler機制的,自然就能看懂.當postResult()方法執行後,InternalHandler
會呼叫AsyncTask的finish()方法.

finish()方法

    private void finish(Result result) {
        //判斷任務是否被取消,如果被取消則回撥onCalled()
        if (isCancelled()) {
            onCancelled(result);
        } else {
            //請求成功
            onPostExecute(result);
        }
        //變更AsyncTask的狀態
        mStatus = Status.FINISHED;
    }

finish()是返回結果的方法,不管AsyncTask是否被取消都會將該AsyncTask的狀態變更成FINISHED.

總結

最後基本上整個AsyncTask的介紹也就清晰了,本文的介紹的思路是:

1.先介紹AsyncTask的核心構成.
2.在介紹AsyncTask的工作流程:
 execute() -->executeOnExecutor() -->postResult() --> finish() 
3.還了解了AsyncTask中的序列和並行的特點,以及AsyncTask實現並行和序列的原理.

AsyncTask內部工作流程圖解

AsyncTask的內部工作邏輯

這圖可能畫的有點複雜,其實仔細對照上文的邏輯和原始碼看,你一定會弄明白的.而且通讀完全文之後,你會發現AsyncTask並不難,原來是這麼簡單.

參考文章

1.安卓藝術探索