Android 源碼解析之AsyncTask

AsyncTask相信你們都不陌生,它是爲了簡化異步請求、更新UI操做而誕生的。使用它不只能夠完成咱們的網絡耗時操做,並且還能夠在完成耗時操做後直接的更新咱們所須要的UI組件。這使得它在android開發中成爲煊赫一時的網絡請求工具類。android

而今天咱們就以源碼分析的形式來完全的學習下它的實現過程。數組

首先,咱們先看看AsyncTask的定義形式:ruby

public abstract class AsyncTask<Params, Progress, Result> {
}

首先AsyncTask它是一個抽象類,包括三種泛型類型,具體含義以下:微信

  • Params:它表示請求參數的類型
  • Progress:執行任務的進度類型
  • Result:返回結果的類型

可是以上三個參數並非必定必須,在不須要時能夠設置爲Void,沒有返回類型。網絡

而後咱們看看它的執行過程,包括如下幾個方法:異步

  • execute(Params... params),咱們在執行異步操做時會調用該方法,表示開始執行任務。ide

  • protected void onPreExecute() {},在調用execute方法後,該方法就會獲得執行,它執行在UI線程中,用來初始化一些UI空間等工具

  • protected abstract Result doInBackground(Params... params);在onPreExecute執行完後將會執行該方法,它執行在後臺,並接受一個Params類型的數組參數,用於請求網絡,而且它返回一個Result 類型的結果。該方法中能夠在執行網絡請求的同時更新請求進度,調用publishProgress(Progress... values) 。oop

  • protected void onProgressUpdate(Progress... values) ,假如在doInBackground方法中調用了publishProgress方法,那麼該方法就會獲得執行,它是執行在UI線程的,根據values的值不停的更改進度,以達到想要的效果。源碼分析

  • protected void onPostExecute(Result result),該方法是在doInBackground方法執行完畢後獲得執行,可根據doInBackground返回的結果進行後續的UI操做,因而可知它是工做在UI線程中的。

通過上面一系列的方法運轉,一個完整的AysncTask請求就正式的完成了任務。不只完成了耗時操做還更新的UI組件,這就是它的魅力所在。可是這時候你該有疑問了,上面的方法都是你說執行哪一個就執行哪一個,哪究竟是怎麼執行的呢?

那麼接下來就正式的揭開它的廬山正面目。

在正式介紹它的源碼以前,你必須知道new 一個類它所執行的過程:

  • 在new的過程當中,它會首先一層一層的加載它所繼承的父類的成員變量及構造方法

  • 而後在加載本身的成員變量和構造方法。

順序是不可變得。

那麼看看在咱們執行 new AsyncTask()中,它到底加載了哪些成員呢?

private static final int CPU_COUNT =          Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

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

    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

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

    private static class SerialExecutor implements Executor{...}
    
    public enum Status {
        PENDING,
        RUNNING,
        FINISHED,
    }

看到這麼一大堆是否是很麻頭皮,其實仔細拆分下來,你主要看幾個變量便可。

  • THREAD_POOL_EXECUTOR :這個成員變量從它THREAD_POOL_EXECUTOR = new ThreadPoolExecutor中能夠看出,它是一個線程池,而ThreadPoolExecutor線程池中須要幾個參數,如corePoolSize(核心線程數)、maximumPoolSize(最大線程數)、workQueue(任務隊列)、threadFactory(線程工程)等等,因此像CORE_POOL_SIZE,sPoolWorkQueue ,sThreadFactory 等成員變量,只是爲了配置這個線程池而已。

  • sDefaultExecutor 這個成員變量是默認的線程調度任務,從上面可看出SERIAL_EXECUTOR則是一個序列化的任務調度,從sDefaultExecutor = SERIAL_EXECUTOR;中能夠明確的知道sDefaultExecutor任務調度中是按前後順序執行的。

  • sHandler顧名思義是一個handler,mWorker是一個工做線程,mFuture則是一個FutureTask,FutureTask是專門用於管理Runnable線程的,mStatus 則是一個枚舉,裏面有三種狀態,分別是未執行,執行中,以及執行完成狀態,默認狀態是未執行狀態。

因此咱們只要理解好上面幾個變量能夠不用懼怕它一堆的初始化成員。

而後咱們在看看AysncTask的構造方法中具體作了那些事:

public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(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);
                }
            }
        };
    }

簡單來講,AsyncTask的成員變量中就只是初始化了兩個變量,mWorker 和 mFuture 。這兩個變量是很是重要的,後續的全部執行過程都是由這兩個變量構成或引導的。

首先mWorker 是一個抽象內部類實例,是一個任務線程,它實現Callable 接口中的call()方法

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

而後mFuture 則是一個針對任務線程的管理類。專門用於管理任務線程的,可使咱們的任務獲得更好的控制,來看看它的構造方法吧:

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

就是接受了咱們的mWorker 對象以及把自身的狀態設置爲NEW。

以上就是在new一個AsyncTask所進行的全部操做,無非就是初始化了一些數據和變量。

下面來看看AysncTask的正式執行。

咱們所知道開啓一個AsyncTask任務所調用的方法是execute方法,該方法必須在主線程中調用。

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

在調用execute方法後,該方法什麼也沒作,只是把已初始化好的默認序列任務線程sDefaultExecutor和傳遞進來的數據params傳遞給了executeOnExecutor(),那麼咱們在看看這個方法作了哪些事情:

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

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }
  • executeOnExecutor()方法中首先判斷了AsyncTask的執行狀態,若是是正在執行或是已經結束執行了,它就會報出一個IllegalStateException的異常,告訴你線程或是在執行中或是已經執行完畢了。

  • 只有在未執行的狀態下,才能夠進行AsyncTask請求任務,接下來它直接把AsyncTask的執行狀態更改成Status.RUNNING,告訴其餘任務該AsyncTask正在執行中,保持執行結果的一致性。而後就執行了onPreExecute();因爲execute方法是必須在主線程中執行的,因此到目前爲止仍是在主線程中運行,也就證實了onPreExecute()方法是在主線程中運行的。

protected void onPreExecute() {
  }

onPreExecute源碼中並無作什麼事情,這對於咱們來講,只須要重寫該方法就能夠在主線程中進行一些UI組件的初始化等操做。

  • 接下來則是將咱們所傳遞的數據賦值給mWorker的mParams變量,而後調用exec.execute(mFuture)方法,咱們經過execute方法中知道exec其實就是一個sDefaultExecutor,sDefaultExecutor實則是一個SerialExecutor 序列線程,而mFuture咱們在構造方法中也很清楚的知道,它是一個封裝了mWorker線程的一個可管理的任務線程,那麼在調用sDefaultExecutor的execute方法並傳遞進了mFuture任務線程,那到底作了什麼事情呢,咱們來看下它的源碼:
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);
            }
        }

源碼咱們很清晰的知道在execute方法中最終的目的就是把mFuture任務線程賦值給一個Runnable 線程並放到了THREAD_POOL_EXECUTOR線程池中,由THREAD_POOL_EXECUTOR線程池來執行mFuture線程任務。

那麼接着咱們看看在THREAD_POOL_EXECUTOR線程池中execute的方法中主要作了什麼事情:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

THREAD_POOL_EXECUTOR線程池中主要是判斷了傳遞的線程是否爲空,是否小於當前線程池中保存的核心線程數,若是小於則直接執行addWorker(command, true)方法,下面看看addWorker方法中的實現內容:

private boolean addWorker(Runnable firstTask, boolean core) {
        ...(前面代碼省略)
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

咱們只看主要的邏輯,首先是將咱們的mFuture任務線程存放到了一個Worker的對象中,而後又從Worker對象中獲取到mFuture線程並賦值給了Thread ,接着把Worker對象放到workers的HashSet數據集合對象中,通過獲取HashSet的大小並進行一些判斷,把workerAdded 設置爲true,最後開啓t.start();線程,由此進入了子線程中

那麼接下來在開啓的子線程中又作了什麼事情呢?

咱們從上面的分析指導t.start()開啓就是一個mFuture的異步任務線程,那麼它在哪執行呢?

細心的朋友能夠發現,原來是在SerialExecutor 中的execute方法中咱們的mFuture的run()早已在等待了線程的啓動,那麼,我如今去看看mFuture的run()方法中作了什麼工做吧

public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

這段代碼很簡單,一眼就能夠看得出來就是利用咱們在爲mFuture初始化時傳遞的mWorker 對象實例並調用它的call()方法,咱們先無論call怎麼實現的,先來看看這個方法中的後續是什麼。

接着它獲得一個執行結果,並把一個boolean類型的ran設置爲true,最後根據ran調用set(result);方法,並把結果傳遞進去,下面看看set的源碼:

protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

它主要調用了finishCompletion();在來看看finishCompletion的源碼:

private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }

在執行完call中,把一些對象進行還原,還調用了 done(),該方法就是在AsyncTask構造方法中咱們有看到它的實現:

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

咱們也說過FutureTask主要是用來管理異步線程任務的,那麼在done方法中就有很好的體現,在該方法中,它會判斷執行的結果是否成功,成功後有沒有被髮送,若是有發送它就再也不發送消息,若是結果執行成功,但沒有被髮送它就會發送最終的執行結果:

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

postResult方法的內容咱們推後一點講,那麼如今咱們來看看mWorker 中call()是怎麼實現的:

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

在這裏咱們終於見到了咱們所熟悉的一個方法doInBackground(),由此也能夠知道其確實是在子線程運行的,而doInBackground()方法在AsyncTask類中是一個抽象方法:

protected abstract Result doInBackground(Params... params);

那麼咱們在重寫doInBackground()時就能夠直接的在其中進行一些耗時的網絡和IO操做了。

這裏插上一句,假如在doInBackground()調用了publishProgress方法來更新進度的話,那來看看它是怎麼作的:

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

publishProgress方法中主要是經過Hangler發送一條更新進度的標誌用來更新進度。這裏的Hangler接受消息在下面和執行結果一塊兒講。

最後doInBackground()執行獲取的Result 結果也將會傳遞到postResult(result);方法中,那麼如今咱們來看看它的源碼實現:

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
    
  private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

  private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

postResult中首先封裝了doInBackground異步執行結果的AsyncTaskResult對象,而後獲取到一個Handler ,經過消息處理機制發送一條信息來切換到主線程中進行UI界面的更換,消息處理機制不屬於本次博文的內容因此再也不細說,那來看看這個Handler是怎麼處理這個消息內容的。

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

Handler中主要是根據消息標誌進行區分是更新進度仍是執行結果:

若是是更新進度則調用AsyncTask的onProgressUpdate方法來更新內容,因爲經過Handler已轉變爲主線程中,因此咱們在重寫該方法時能夠直接更新UI組件。

若是是執行結果則AsyncTask的finish(result.mData[0]);並把結果數據傳遞過去,來看看finish()中是怎麼實現的:

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

finish()方法中也很是的簡單,首先判斷是否爲取消線程,不然的話則執行onPostExecute(result)方法,由此咱們在重寫了onPostExecute方法後能夠直接的更新咱們的UI組件。

最後把AsyncTask的狀態改成完成狀態,至此整個AsyncTask生命週期就執行完畢了。

好了,至此AsyncTask整個執行過程就徹底講完了,相信你們也學到了很多東西,建議你們有空本身對着源碼在梳理一遍,畢竟本身總結出來的印象就更深入。

今天就到這裏吧,祝你們生活愉快。

更多資訊請關注微信平臺,有博客更新會及時通知。愛學習愛技術。
這裏寫圖片描述

相關文章
相關標籤/搜索