Thread并發請求封裝 - 深入理解AsyncTask類

jopen 10年前發布 | 28K 次閱讀 AsyncTask Android開發 移動開發

本篇文章主要面向有一定Android基礎的人,如果你還剛入門,這篇文章看起來可能會比較吃力,希望你能學到新東西。

在Android開發中,由于不能再UI線程中做耗時操作,常常需要開啟線程來做一些操作。但是這樣一來就產生了一個問題,就是大量的線程并發執行,造成了線程維護的開銷進而使得代碼質量下降手機發燙又耗電。讓我們來看一下KJFrameForAndroid框架是如何解決這個問題的。
KJFrameForAndroid框架項目地址:https://github.com/kymjs/KJFrameForAndroid

其實Android提供了一套專門用于異步處理的類,就是我們熟悉又模式的AsynTask類。
AsynTask類就是對Thread類的一個封裝,并且加入了一些新的方法。那么我們先來看一下它對于Thread請求的一系列管理與封裝。
在jdk中有這樣一個集合類叫:BlockingQueue<>它是一個 Queue<E>的子類,支持兩個附加操作的 Queue,這兩個操作是:獲取元素時等待隊列變為非空,以及存儲元素時等待空間變得可用。
BlockingQueue 方法以四種形式出現,對于不能立即滿足但可能在將來某一時刻可以滿足的操作,這四種形式的處理方式不同:第一種是拋出一個異常,第二種是返回一個特殊值(null 或 false,具體取決于操作),第三種是在操作可以成功前,無限期地阻塞當前線程,第四種是在放棄前只在給定的最大時間限制內阻塞。

Thread并發請求封裝——深入理解AsyncTask類

我們來看一下在jdk中關于這個類的介紹:

BlockingQueue 不接受 null 元素。試圖 add、put 或 offer 一個 null 元素時,某些實現會拋出 NullPointerException。null 被用作指示 poll 操作失敗的警戒值。

好,為什么要講這個集合類呢?因為這與我們接下來要講的線程池維護有著莫大的關系。

    // 靜態阻塞式隊列,用來存放待執行的任務,初始容量:8個
    private static final BlockingQueue<Runnable> mPoolWorkQueue = new LinkedBlockingQueue<Runnable>(8);

這里是KJFrameForAndroid開發框架中對于線程隊列的維護對象,通過這個線程隊列將所有線程放置在一個靜態阻塞式隊列中保存起來。
然而,僅僅是保存起來還不夠,因為線程需要執行,我們必須要讓線程進入到CPU工作間才行,這時KJFrameForAndroid使用了jdk中的另一個類ThreadPoolExecutor來管理并使用并發啟動這些線程
來看一下在KJFrameForAndroid中對于這個類對象的創建

    /**
     * 并發線程池任務執行器,可以用來并行執行任務<br>
     * 與mSerialExecutor(串行)相對應
     */
    public static final Executor mThreadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
            TimeUnit.SECONDS, mPoolWorkQueue, mThreadFactory);

通過注釋,我們還可以看到KJFrameForAndroid框架其實還有一種并行執行線程的方式,這里暫時不講,我們看看這個類的構造方法在jdk中的解釋:

Thread并發請求封裝——深入理解AsyncTask類

那么至此我們有了并發任務執行器和線程池隊列,最后就只需要將線程執行起來就行了。ThreadPoolExecutor類是一個實現了執行器Executor接口的類,那么它也就有一個execute方法去執行線程,我們所要做的就是調用這個類來執行它就可以了。

接下來我們來看一下如何讓線程執行完成后又回到UI線程去做界面處理的。

還是看源碼,以下是摘自KJFrameForAndroid開源框架中的一段代碼:

/*********************** start 一個完整的執行周期 ***************************/
    /**
     * 在doInBackground之前調用,用來做初始化工作 所在線程:UI線程
     */
    protected void onPreExecute() {}
    /**
     * 這個方法是我們必須要重寫的,用來做后臺計算 所在線程:后臺線程
     */
    protected abstract Result doInBackground(Params... params);
    /**
     * 打印后臺計算進度,onProgressUpdate會被調用<br>
     * 使用內部handle發送一個進度消息,讓onProgressUpdate被調用
     */
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            mHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new KJTaskResult<Progress>(this, values))
                    .sendToTarget();
        }
    }
    /**
     * 在publishProgress之后調用,用來更新計算進度 所在線程:UI線程
     */
    protected void onProgressUpdate(Progress... values) {}
    /**
     * 任務結束的時候會進行判斷:如果任務沒有被取消,則調用onPostExecute;否則調用onCancelled
     */
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
    /**
     * 在doInBackground之后調用,用來接受后臺計算結果更新UI 所在線程:UI線程
     */
    protected void onPostExecute(Result result) {}
    /**
     * 所在線程:UI線程<br>
     * doInBackground執行結束并且{@link #cancel(boolean)} 被調用。<br>
     * 如果本函數被調用則表示任務已被取消,這個時候onPostExecute不會再被調用。
     */
    protected void onCancelled(Result result) {}
    /*********************** end 一個完整的執行周期 ***************************/

在這里我們看到了三個非常熟悉的函數:doInBackground、onPostExecute、onProgressUpdate
沒錯,這三個函數正式SyncTask中被我們使用的三個方法(如果你還不知道,請搜索Android開發中SyncTask用法)接著我們再看看他們是如何被調用的:
這里聲明了一個線程任務被執行的方法,實際上也就是doInBackground、被調用的方法:

/**
     * 必須在UI線程調用此方法<br>
     * 通過這個方法我們可以自定義KJTaskExecutor的執行方式,串行or并行,甚至可以采用自己的Executor 為了實現并行,
     * asyncTask.executeOnExecutor(KJTaskExecutor.mThreadPoolExecutor, params);
     */
    public final KJTaskExecutor<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)");
            default:
                break;
            }
        }
        mStatus = Status.RUNNING;
        onPreExecute();
        mWorker.mParams = params;
        exec.execute(mFuture);// 原理{@link #execute(Runnable runnable)}
        // 接著會有#onProgressUpdate被調用,最后是#onPostExecute
        return this;
    }

而onProgressUpdate實際上是在這個handle中被調用了。

/**
     * KJTaskExecutor內部Handler,用來發送后臺計算進度更新消息和計算完成消息
     */
    private static class InternalHandler extends Handler {
        @Override
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public void handleMessage(Message msg) {
            KJTaskResult result = (KJTaskResult) 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;
            }
        }
    }

有關本類的完整代碼可以查看https://github.com/kymjs/KJFrameForAndroid/blob/master/KJLibrary/src/org/kymjs/aframe/core/KJTaskExecutor.java

來自:http://my.oschina.net/kymjs/blog/313744

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!