深入理解AsyncTask

主線程和子線程

主線程是指進(jìn)程所擁有的線程,在Java中默認(rèn)一個(gè)進(jìn)程只有一個(gè)線程,這個(gè)線程就是主線程。主線程主要用于處理界面交互相關(guān)的邏輯,因?yàn)橛脩綦S時(shí)都可能會(huì)和界面發(fā)生交互,因此主線程在任何時(shí)候都必須要有較快的響應(yīng)速度,否則就會(huì)給人一種卡頓的感覺(jué)。這就要求主線程不能處理太耗時(shí)的任務(wù),比如網(wǎng)絡(luò)請(qǐng)求、IO操作等。這樣,只能把網(wǎng)絡(luò)請(qǐng)求、IO這些耗時(shí)操作放到子線程中去處理。所以,正常一個(gè)帶有網(wǎng)絡(luò)接口請(qǐng)求的應(yīng)用,那么至少會(huì)需要兩個(gè)線程,一個(gè)主線程負(fù)責(zé)處理交互,一個(gè)子線程負(fù)責(zé)處理接口請(qǐng)求,那么子線程請(qǐng)求接口之后拿到的數(shù)據(jù),怎么傳遞給主線程去更新UI呢?我們可以用Handler來(lái)處理子線程與主線程的交互。而AsyncTask就是封裝了Handler+線程池來(lái)處理子線程與主線程交互的一個(gè)API,方便了開(kāi)發(fā)者,不用每次都自己創(chuàng)建線程和寫Handler了。

AsyncTask基本使用

創(chuàng)建一個(gè)下載文件的異步任務(wù)

private class DownloadFileTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}

執(zhí)行該異步任務(wù)

DownloadFileTask downloadTask = new DownloadFileTask().execute(url1,url2,url3);

AsyncTask是一個(gè)抽象的泛型類,主要接受三個(gè)參數(shù),分別是Params、Progress、Result,其中Params主要是AsyncTask執(zhí)行時(shí)傳遞進(jìn)來(lái)的參數(shù)類型,Progress是在異步任務(wù)執(zhí)行的過(guò)程中的參數(shù)類型,Result是異步任務(wù)結(jié)束之后的類型。另外,AsyncTask提供了4個(gè)核心方法,分別如下:

  • onPreExecute(),在主線程中執(zhí)行,在異步任務(wù)開(kāi)始執(zhí)行之前調(diào)用,一般用于任務(wù)執(zhí)行前的準(zhǔn)備工作
  • doInBackground(Params... param),在線程池中執(zhí)行,開(kāi)始執(zhí)行異步執(zhí)行任務(wù),param是通過(guò)AsyncTask的execute方法傳遞進(jìn)來(lái)的,在這個(gè)方法中可以通過(guò)publishProgress來(lái)更新任務(wù)的進(jìn)度,這個(gè)方法會(huì)調(diào)用到onProgressUpdate
  • onProgressUpdate(Progress... progress),在主線程中執(zhí)行,用于更新進(jìn)度。
  • onPostExecute(Result result),在主線程中執(zhí)行,異步任務(wù)執(zhí)行結(jié)束之后執(zhí)行,其中result參數(shù)是doInBackground的返回值。

取消AsyncTask異步任務(wù)

downloadTask.cancel(true);

注意:這里的cancel方法的參數(shù)既可以傳true也可以傳false,其中true表示可以中斷正在執(zhí)行的任務(wù)。另外,雖然調(diào)用了這個(gè)cancel方法,但是doInbackground的異步任務(wù),并不會(huì)立即結(jié)束,只是不會(huì)調(diào)用onProgressUpdate和onPostExecute。舉個(gè)例子,doInBackground中有個(gè)循環(huán),當(dāng)執(zhí)行cancel并不會(huì)中斷這個(gè)循環(huán),如果需要中斷這個(gè)循環(huán),那么可以在doInBackground中添加一個(gè)判斷邏輯,如果isCancelled()則return。

AsyncTask在具體的使用過(guò)程中也有一些條件限制,主要有如下幾點(diǎn):

  • AsyncTask這個(gè)類必須在主線程中加載,不過(guò)這個(gè)在Android4.1及以上版本中已經(jīng)被系統(tǒng)自動(dòng)完成。
  • 必須在主線程創(chuàng)建該實(shí)例對(duì)象
  • 必須在主線程調(diào)用execute(Params...)
  • 不要手動(dòng)調(diào)用上面的四個(gè)核心方法
  • 一個(gè)AsyncTask任務(wù)只能execute一次,否則會(huì)報(bào)運(yùn)行時(shí)異常

AsyncTask的發(fā)展史
在一開(kāi)始出現(xiàn)AsyncTask時(shí),只有一個(gè)子線程來(lái)執(zhí)行異步任務(wù)。從Android1.6開(kāi)始,從單線程改成了線程池了支持并發(fā)任務(wù)了。然后從3.0開(kāi)始為了解決并發(fā)導(dǎo)致的錯(cuò)誤又改成了串行執(zhí)行了。如果你還是想并發(fā)執(zhí)行任務(wù),可以調(diào)用AsyncTask的executeOnExecutor方法。

AsyncTask的工作原理

為了分析AsyncTask的工作原理,我們先分析它的execute方法,execute方法又調(diào)用了executeOnExecutor方法,它們的實(shí)現(xiàn)如下:

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

   @MainThread
    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;
    }

可以看到,在這里已經(jīng)開(kāi)始調(diào)用了onPreExecute方法,然后把Params封裝到FutureTask對(duì)象中了,最后開(kāi)始執(zhí)行sDefaultExecutor這個(gè)默認(rèn)線程池。下面分析這個(gè)線程池的執(zhí)行過(guò)程,代碼如下:

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

從SerialExecutor的實(shí)現(xiàn)可以分析出多個(gè)AsyncTask同時(shí)執(zhí)行默認(rèn)是串行的。SerialExecutor首先會(huì)將FutureTask插入到隊(duì)列中,如果發(fā)現(xiàn)沒(méi)有正在執(zhí)行的Task,那么就從隊(duì)列中取出一個(gè)Task放到THREAD_POOL_EXECUTOR線程池中執(zhí)行,當(dāng)執(zhí)行完成之后再會(huì)調(diào)用scheduleNext,直到mTasks中的Task都執(zhí)行完成為止。FutureTask已經(jīng)放到THREAD_POOL_EXECUTOR線程池中了,那么就會(huì)開(kāi)始執(zhí)行這個(gè)FutureTask了,F(xiàn)utureTask的實(shí)現(xiàn)如下:

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    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 {
                    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的執(zhí)行從call方法開(kāi)始,在call中調(diào)用了第二個(gè)核心方法,就是doInBackground,然后拿到doInBackground的返回值通過(guò)postResult(result)傳遞給AsyncTask的另外一個(gè)重要角色I(xiàn)nternalHandler,這樣就通過(guò)Handler機(jī)制實(shí)現(xiàn)了從子線程傳遞數(shù)據(jù)到主線程了,然后在Handler中執(zhí)行了第四個(gè)重要方法onPostExecute來(lái)更新UI。InternalHandler實(shí)現(xiàn)如下:

private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

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

可以發(fā)現(xiàn)InternalHandler是個(gè)靜態(tài)的內(nèi)部類,為了能夠?qū)?zhí)行環(huán)境切換到主線程,這就要求創(chuàng)建的Handler對(duì)象必須在主線程創(chuàng)建。由于靜態(tài)成員必須在類加載時(shí)初始化,所以才會(huì)要求AsyncTask的類必須在主線程中加載。

參考:
https://developer.android.com/reference/android/os/AsyncTask
《Android開(kāi)發(fā)藝術(shù)探索-任玉剛》

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

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

  • AsyncTask 是一個(gè)簡(jiǎn)單實(shí)用的多線程異步任務(wù)工具類。 Android 開(kāi)發(fā)中經(jīng)常遇到需要將耗時(shí)的操作放到子線...
    HJDaryl閱讀 805評(píng)論 0 1
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 3,429評(píng)論 1 15
  • Android開(kāi)發(fā)者:你真的會(huì)用AsyncTask嗎? 導(dǎo)讀.1 在Android應(yīng)用開(kāi)發(fā)中,我們需要時(shí)刻注意保證...
    cxm11閱讀 2,776評(píng)論 0 29
  • 目錄介紹 01.先看下AsyncTask用法 02.AsyncTask源碼深入分析2.1 構(gòu)造方法源碼分析2.2 ...
    楊充211閱讀 676評(píng)論 0 3
  • 在Android中我們可以通過(guò)Thread+Handler實(shí)現(xiàn)多線程通信,一種經(jīng)典的使用場(chǎng)景是:在新線程中進(jìn)行耗時(shí)...
    呂侯爺閱讀 2,178評(píng)論 2 23

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