AsyncTask源碼閱讀筆記

寫在前面

感覺最近自己需要多讀書,所以在以后的一段時間里可能都是筆記形式的文了,希望自己能厚積薄發(fā)吧。

AsyncTask簡介

AsyncTask是一個輕量級的異步任務(wù)類,允許你將一個耗時操作放在后臺進行,并且會返回操作的結(jié)果給你。那么AsyncTask和Thread-Handler或者線程池有什么異同呢?

在AsyncTask的源碼注釋里這樣描述:

/**
 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to
 * perform background operations and publish results on the UI thread without
 * having to manipulate threads and/or handlers.</p>
 */

AsyncTask能讓更加簡便的使用UI線程。這個類允許執(zhí)行后臺操作和將結(jié)果發(fā)送到UI線程而不必操作線程和handlers。

讀了上面的注釋讓我們對AsyncTask有了一定的了解,這是個方便我們的類,讓我們在后臺執(zhí)行操作結(jié)果而不必自己手動的去切換線程,那么這個類是否有其他的限制呢?

 /** <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
  * and does not constitute a generic threading framework. AsyncTasks should ideally be
  * used for short operations (a few seconds at the most.) If you need to keep threads
  * running for long periods of time, it is highly recommended you use the various APIs
  * provided by the <code>java.util.concurrent</code> package such as {@link Executor},
  * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
  */

AsyncTask被設(shè)計作為Thread和Handler間的幫助類,并非構(gòu)建線程框架的通用類。AsyncTask理想情況下應(yīng)該被用來進行短時間的操作,如果你需要保證線程長期運行,那么強烈推薦你使用java.util.concurrentpackage提供的多種API,例如Executor、ThreadPoolExecutor和FutureTask。

簡單的演示

上面簡單的介紹了一下AsyncTask,下面看一下如何使用。AsyncTask提供了4個核心方法:

  • onPreExecute(),在主線程中執(zhí)行,在后臺任務(wù)執(zhí)行之前,此方法會被調(diào)用

  • doInBackground(Params... params),此方法用于執(zhí)行需要執(zhí)行的異步任務(wù)

  • onProgressUpdata(Progress... values),在主線程中執(zhí)行,當(dāng)后臺任務(wù)的執(zhí)行進度發(fā)生改變時此方法會被調(diào)用

  • onPostExecute(Result result),在主線程中執(zhí)行,返回操作結(jié)果,返回類型是doInBackground的返回值

        new AsyncTask<String, Integer, Bean>() {

            @Override
            protected void onPreExecute() {//做一些準(zhǔn)備工作
                super.onPreExecute();
            }

            @Override
            protected Bean doInBackground(String... params) {//后臺任務(wù)
                return null;
            }

            @Override
            protected void onProgressUpdate(Integer... values) {//后臺任務(wù)執(zhí)行進度發(fā)生變化
                super.onProgressUpdate(values);
            }

            @Override
            protected void onPostExecute(Bean aLong) {//異步任務(wù)執(zhí)行完成返回結(jié)果
                super.onPostExecute(aLong);
            }
        }.execute(url1,url2);

上面的代碼只是一個簡單的實例,并非真的演示如何使用。以上代碼中的三個參數(shù)可以理解為url、進度和自定義的數(shù)據(jù)類型。帶入我們平時的開發(fā)中就是根據(jù)url拿到數(shù)據(jù),然后在界面上更新進度。在方法中這樣String... params表示不定數(shù)量的參數(shù),是數(shù)組型的參數(shù)。上面的方法實際上運行的效果是串行執(zhí)行的,你可能會說AsyncTask內(nèi)部不是封裝了一個線程池嗎?為毛會是串行的?這個問題先留著,先把結(jié)論擺在這,而且我也在AsyncTask的源碼中看到了如下的注釋(原文不放了,感興趣的請自己去看):

調(diào)度任務(wù)是用隊列單獨的調(diào)度一個后臺線程還是用線程池取決于平臺版本。剛發(fā)布的時候,AsyncTask是以串行線程的方式執(zhí)行的。從Android DONUT(1.6)開始允許多任務(wù)并發(fā)執(zhí)行。在Android HONEYCOMB(3.0)之后又變成了單任務(wù)串行執(zhí)行,這是為了避免由于并發(fā)操作可能帶來的錯誤。如果你真的想要并發(fā)執(zhí)行,你可以使用excuteOnExecutor和THREAD_POOL_EXECUTOR。

好了,讀到這,終于對AsyncTask有了一些了解了,帶著一些問題去看看源碼吧。

源碼筆記

讀源碼先從AsyncTask的入口execute()開始看:

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

調(diào)了一個方法,沒啥好說的,跟進去看就行了,這里返回值是AsyncTask,方便我們持有一個AsyncTask的引用。

    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;
        //調(diào)用這個方法,如果你有實現(xiàn),那么你的代碼將在這被執(zhí)行
        onPreExecute();
        //拿到參數(shù)
        mWorker.mParams = params;
        //開始運行
        exec.execute(mFuture);

        return this;
    }

這里先看mStatus,這東西是個枚舉類型,里面仨值分別是:

  • PENDING:任務(wù)還沒有被執(zhí)行過,表示可以被執(zhí)行
  • RUNNING:任務(wù)正在執(zhí)行
  • FINISHED:任務(wù)已經(jīng)完成

從代碼中可以看出來只有這個值是PENDING的時候才會被執(zhí)行,其他的值都會報異常,這就是AsyncTask對象只能運行一次的由來了,每次執(zhí)行任務(wù)都需要新建一個AsyncTask對象(準(zhǔn)確的來說是子類對象)。

在運行之后將mStatus的值改為RUNNING,之后這個對象就不能在其他的地方被執(zhí)行了。可以看到在任務(wù)真正被執(zhí)行之前調(diào)用了onPreExecute()方法,這就是這個方法可以做一些準(zhǔn)備工作的原因。

之后先獲取參數(shù),再執(zhí)行。這里先簡單的說說,要弄懂最后這句exec.execute(mFuture);代碼還需要結(jié)合前面的代碼來看。

之前在前面說了AsyncTask內(nèi)部有兩個線程池,那么他要干啥為毛要兩個線程池呢?因為一個線程池是串行的線程池,一個進程中的所有待執(zhí)行的任務(wù)都會在這個串行的線程池中排隊執(zhí)行。接下來看一下AsyncTask內(nèi)部的倆線程池在代碼里長啥樣:

    /**
     * 可以并發(fā)執(zhí)行任務(wù)
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * 在串行命令下一次執(zhí)行一個任務(wù)。
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

上面那個線程池是真正用來執(zhí)行任務(wù)的,下面的是用來排隊等待的??梢郧宄目吹竭@倆是靜態(tài)的,所以是全局共享這個就不做過多的解釋了。那么按順序來,從下面的線程池的execute()來看:

    private static class SerialExecutor implements Executor {
        //雙端隊列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //將任務(wù)插入到任務(wù)隊列中
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //如果當(dāng)前沒有正在執(zhí)行的任務(wù),那么執(zhí)行下一個AsyncTask任務(wù)
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {//取出這個任務(wù)
                //放入并發(fā)線程池中執(zhí)行
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

從上面代碼的流程我們可以認(rèn)識到在我們不指定線程池的情況下,的確,我們的代碼是以串行的方式被執(zhí)行的。關(guān)于mFuture其真實類型是FutureTask,對此我們不需要再做更多的了解(其實我了解的也不多...),當(dāng)然了如果你對Java的并發(fā)編程感興趣可以自己去做更多的了解。在這我們需要知道的就是mFuture的run方法會調(diào)用mWorker的call方法,因此mWorker的call方法最終會在線程池中執(zhí)行。

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //表示當(dāng)前任務(wù)已經(jīng)被調(diào)用過了
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //執(zhí)行我們的代碼
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                //發(fā)送操作結(jié)果
                return postResult(result);
            }
        };

以上可以看到我們希望在后臺執(zhí)行的代碼被調(diào)用了,并且結(jié)果被postResult這個方法發(fā)送了,跟進去看看:

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

直接看這個消息在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:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

在這可以看到這個Handler是一個靜態(tài)類,靜態(tài)類會在類被加載的時候就被初始化,而Handler的初始化時需要looper()的,所以這就需要你在主線程中使用AsyncTask。否則要么出錯,要么AsyncTask就被你廢了。好了,繼續(xù)看,在對應(yīng)的情況底下調(diào)用了AsyncTask的finish方法,看下是啥:

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

如果被取消,就調(diào)取消的方法,不然的話就回調(diào)這個onPostExecute(result),由于是經(jīng)過handler發(fā)送的,所以線程已經(jīng)切換到了AsyncTask調(diào)用的線程中去了(關(guān)于這個如果你有不明白可以看我的Android消息機制淺析),我們就可以在主線程中開心的使用這個結(jié)果去更新UI了。

小結(jié)

  • AsyncTask用起來比較方便,但是特別耗時的操作并不適合用它來執(zhí)行。
  • AsyncTask默認(rèn)是串行執(zhí)行,但是你可以通過指定執(zhí)行的線程池來讓他并發(fā)執(zhí)行。
  • AsyncTask對象只能被執(zhí)行一次。
  • AsyncTask使用Handler來切換線程。
  • AsyncTask一般情況下都是需要在主線程被實現(xiàn)和調(diào)用的。
  • AsyncTask在不同版本的Android上可能會有不同的表現(xiàn),但是現(xiàn)在用戶Android版本普遍在4.0以上,這個就無需考慮了。

參考資料:
《Android開發(fā)藝術(shù)探索》
源碼版本:Android 7.0 api 24

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,036評論 25 709
  • Android Handler機制系列文章整體內(nèi)容如下: Android Handler機制1之ThreadAnd...
    隔壁老李頭閱讀 3,420評論 1 15
  • 美圖欣賞 Java、Android知識點匯集 Java集合類 ** Java集合相關(guān)的博客** java面試相關(guān) ...
    ElvenShi閱讀 1,884評論 0 2
  • 雖說現(xiàn)在做網(wǎng)絡(luò)請求有了Volley全家桶和OkHttp這樣好用的庫,但是在處理其他后臺任務(wù)以及與UI交互上,還是需...
    weishu閱讀 3,506評論 10 55
  • 時間飛快 忽爾已入秋 這一年不知不覺已過大半 用照片記錄些許點滴 和這個夏天告別 一個人在這世上從最初的很多離不開...
    陳叔深閱讀 371評論 0 0

友情鏈接更多精彩內(nèi)容