Android 源碼解析之AsyncTask

來源:互聯網
上載者:User

標籤:incr   控制   取消   start   分析   seo   抽象方法   網路請求   spool   

AsyncTask相信大家都不陌生,它是為了簡化非同步請求、更新UI操作而誕生的。使用它不僅可以完成我們的網路耗時操作,而且還可以在完成耗時操作後直接的更新我們所需要的UI組件。這使得它在android開發中成為炙手可熱的網路請求工具類。

而今天我們就以源碼分析的形式來徹底的學習下它的實現過程。

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

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

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

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

但是以上三個參數並不是一定必須,在不需要時可以設定為Void,沒有傳回型別。

然後我們看看它的執行過程,包括以下幾個方法:

  • execute(Params... params),我們在執行非同步作業時會調用該方法,表示開始執行任務。

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

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

  • 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

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整個執行過程就完全講完了,相信大家也學到了不少東西,建議大家有空自己對著源碼在梳理一遍,畢竟自己總結出來的印象就更深刻。

今天就到這裡吧,祝大家生活愉快。

更多資訊請關注平台,有部落格更新會及時通知。愛學習愛技術。

Android 源碼解析之AsyncTask

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.