【安卓筆記】AsyncTask源碼剖析,安卓asynctask源碼

來源:互聯網
上載者:User

【安卓筆記】AsyncTask源碼剖析,安卓asynctask源碼
前言:初學AsyncTask時,就想研究下它的實現源碼,怎奈源碼看了好幾遍都沒看懂,於是擱置了。最近心血來潮,又看了一些源碼,如HandlerThread,IntentService,AsyncQueryHandler等,收穫頗深,於是乎想回頭再研究下AsyncTask,沒想到這次居然很容易看懂了。。。本文:註:1.讀者閱讀本文前,必須對android的Handler機制以及j.u.c中的線程池有所瞭解;2.AsyncTask使用方式不再贅述。3.不同版本AsyncTask內容有些不同.
首先明確AsyncTask是個抽象類別,接受三個泛型參數,分表代表任務所需參數類型,任務進度類型,結果類型。

public abstract class AsyncTask<Params, Progress, Result> 

開發人員繼承AsyncTask後必須重寫doInbackground方法,其他方法如onPostExecute等按需重寫。
其內部有個靜態全域的線程池變數THREAD_POOL_EXECUTOR,AsyncTask的doInbackground中的任務就是由此線程池執行。
public static final Executor THREAD_POOL_EXECUTOR            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

ThreadPoolExecutor各參數含義如下,CORE_POOL_SIZE為核心線程數量,MAXIMUM_POOL_SIZE為線程池最大線程數量,KEEP_ALIVE參數表明當線程池的線程數量大於核心線程數量,那麼空閑時間超過KEEP_ALIVE時,超出部分的線程將被回收,sPoolWorkQueue是任務隊列,儲存的是一個個Runnable,sThreadFactory是線程工廠,用於建立線程池中的線程。所有這些參數在AsyncTask內部都已經定義好:
 private static final int CORE_POOL_SIZE = 5;    private static final int MAXIMUM_POOL_SIZE = 128;    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>(10);

AsyncTask並不是直接使用上述線程池,而是進行了一層“封裝”,這個類就是SerialExecutor,這是個串列的線程池。
/**     * 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 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方法時,先將Runnable封裝一下(即加上try-finally塊)然後排入佇列,接著判斷當前mActive是否為空白,第一次調用,此值為空白,所以會調用scheduleNext方法,從隊列中取出頭部的任務,交給線程池THREAD_POOL_EXECUTOR處理,而處理過程是這樣的,先執行原先的Runnable的run方法,接著再執行scheduleNext從隊列中取出Runnable,如此迴圈,直到隊列為空白,mActive重新為空白為止。我們發現,經過這樣的處理, 所有任務將串列執行所以我們需要注意,如果兩個AsyncTask都調用execute時,如果其中一個AsyncTask任務執行時間非常長,這將導致另一個AsyncTask的任務排隊等候,無法執行,因為它們共用同一個線程池且池是串列執行任務的。
接著看其他成員:
private static final int MESSAGE_POST_RESULT = 0x1;//當前訊息類型--->任務完成訊息    private static final int MESSAGE_POST_PROGRESS = 0x2;//當前訊息類型-->進度訊息    private static final InternalHandler sHandler = new InternalHandler();    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//預設線程池    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();

sDefaultExecutor指明預設的線程池是串列池,mStatus指明當前任務狀態,Status是個枚舉類型:
 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,//執行結束    }

重點看InternalHandler實現:
 private static class InternalHandler extends Handler {        @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,並複寫了handleMessage,該方法根據訊息類型做不同處理,如果任務完成,則調用finish方法,而finish方法根據任務狀態(取消or完成)調用onCancelled或者onPostExecute,這兩個是回調方法,通常我們會在onPostExecute中根據任務執行結果更新UI,這也是為什麼文檔中要求AsyncTask必須在UI線程中建立的原因,我們的Handler須與UI線程的Looper綁定才能更新UI,並且子線程預設是沒有Looper的。
 private void finish(Result result) {        if (isCancelled()) {            onCancelled(result);        } else {            onPostExecute(result);        }        mStatus = Status.FINISHED;    }

如果是更新進度的訊息(MESSAGE_POST_PROGRESS),那麼調用onProgressUpdate方法。
接下來我們關注的問題是 doInbackground方法在何處被調用?聯想AsyncTask的使用,當我們建立好一個AsyncTask執行個體後,我們將調用其execute方法,所以,doInbackground方法應該在execute方法中執行,找到其實現:
 public final AsyncTask<Params, Progress, Result> execute(Params... params) {        return executeOnExecutor(sDefaultExecutor, params);    }

並沒有直接調用doInbackground,而是調用了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方法的作用是使用指定的線程池執行任務,這裡當然使用的是sDefaultExecutor,也就是SerialExecutor,但是我們注意到這個方法是公用的,也就是說 我們可以手動設定線程池,讓AsyncTask並存執行起來,最簡單的方法就是使用內部定義好的THREAD_POOL_EXECUTOR。executeOnExecutor方法首先檢查目前狀態,如果不是Pending狀態則拋出異常,緊接著修改狀態為Running態,接著調用onPreExecute方法(預先處理,相信大家不陌生)。
關鍵代碼是exec.execute(mFuture)這一句,這行代碼的作用是執行mFuture中定義好的任務,mFuture為FutureTask類型,FutureTask是Runnable的實作類別(j.u.c中的),所以可以作為線程池execute方法的參數,我們找到其定義:
 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 occured while executing doInBackground()",                            e.getCause());                } catch (CancellationException e) {                    postResultIfNotInvoked(null);                }            }        };

我們都知道,FutureTask構造時必須傳入一個Callable的實作類別,線程最終執行的是Callable的call方法(不明白的請看java線程並發庫),所以mWorker必然是Callable的實作類別:
  private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {        Params[] mParams;    } mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                //noinspection unchecked                return postResult(doInBackground(mParams));            }        };

最終,我們在WorkRunnable方法中找到了doInBackground方法,曆經艱辛啊!!剩下最後一個問題: 訊息是如何發送給Handler的? 1.任務執行完畢的訊息是通過postResult方法發送的:
 private Result postResult(Result result) {        @SuppressWarnings("unchecked")        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }

這個AsyncTaskResult封裝了資料域和對象本身:
 private static class AsyncTaskResult<Data> {        final AsyncTask mTask;        final Data[] mData;        AsyncTaskResult(AsyncTask task, Data... data) {            mTask = task;            mData = data;        }    }
2.任務進度更新訊息是通過publishProgress方法發送的:
 protected final void publishProgress(Progress... values) {        if (!isCancelled()) {            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();        }    }

至此,AsyncTask源碼分析完畢!

稍微做個 總結1.AsyncTask是對線程池和Handler的封裝,但是預設情況下任務是串列執行的,需注意,多個AsyncTask執行個體共用同一線程池(線程池是static的); 2.高並發任務並不推薦使用AsyncTask,而應該改用線程池; 3.務必在主線程中建立AsyncTask,因為AsyncTask內部的Handler在建立時必須要綁定主線程的Looper; 4.只能在doInBackground方法中執行耗時任務,其他方法如onPreExecute、onPostExecute等運行於主線程上.



支援本機存放區的安卓筆記或記事軟體5個

筆記記事本、手繪筆記、輕筆記、GNotes筆記本、有道筆記 、為知筆記、快速筆記、拍客筆記
 
安卓筆記本

那不是筆記本吧,是平板電腦?,你可以到安卓網看看
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.