八、AsyncTask解析

如果在一個(gè)進(jìn)程中,頻繁創(chuàng)建和銷毀線程,顯然不是高效的做法。正確的做法是采用線程池,一個(gè)線程池中會(huì)緩存一定數(shù)量的線程,通過線程池可以避免因?yàn)轭l繁創(chuàng)建和銷毀線程所帶來的消耗。

1.AsyncTask簡(jiǎn)介

Asynctask是一個(gè)抽象類,它是Android封裝的一個(gè)輕量級(jí)異步類(輕量級(jí)體現(xiàn)在使用方便,代碼簡(jiǎn)潔),它可以在線程池中執(zhí)行后臺(tái)任務(wù),然后把執(zhí)行的進(jìn)度和最終的結(jié)果呈現(xiàn)給主線程,并且更新UI。
Asynctask內(nèi)部封裝了兩個(gè)線程池(SerialExecutorTHREAD_POOL_EXECUTOR),和一個(gè)Handler(IntentHandler)。

  • SerialExecutor線程池用于任務(wù)的排隊(duì),讓需要執(zhí)行的多個(gè)耗時(shí)任務(wù)按順序排列。
  • THREAD_POOL_EXECUTOR線程池才真正執(zhí)行任務(wù)。
  • IntentHandler用于從工作線程切換到主線程
1.1 AsyncTask的泛型參數(shù)

AsyncTask的類型聲明:

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

AsyncTask是一個(gè)抽象的泛型類。

  • Params: 開始異步任務(wù)執(zhí)行時(shí)傳入的參數(shù)類型。
  • Progress:異步任務(wù)執(zhí)行過程中,返回下載進(jìn)度值的類型。
  • Result: 異步任務(wù)執(zhí)行完成,返回的結(jié)果類型。
    如果AsyncTask確定不需要傳遞具體參數(shù),那么這三個(gè)泛型參數(shù)可以用Void來代替。
1.2 AsyncTask的核心方法
  • onPreExecute()(主線程中執(zhí)行):這個(gè)方法會(huì)在后臺(tái)任務(wù)開始執(zhí)行前調(diào)用,在主線程執(zhí)行。用于進(jìn)行一些界面上的初始化操作,比如顯示一個(gè)對(duì)話框列表什么的。
  • doInBackground(Params...)(子線程中執(zhí)行): 這個(gè)方法在子線程中執(zhí)行,負(fù)責(zé)處理耗時(shí)操作。
    任務(wù)一旦完成就可以通過return語(yǔ)句來將任務(wù)的執(zhí)行結(jié)果進(jìn)行返回,如果AsyncTask的第三個(gè)參數(shù)指定的是Void,就可以不返回執(zhí)行結(jié)果。如果要更新UI,可以通過調(diào)用publishProgress(Progess...)方法來完成比如反饋當(dāng)前的進(jìn)度。
  • onProgressUpdate(Progress...)(主線程中執(zhí)行):當(dāng)在后臺(tái)進(jìn)程中調(diào)用publishProgress(Progress...)方法后,這個(gè)方法就會(huì)被調(diào)用,方法中攜帶的參數(shù)就是后臺(tái)任務(wù)中傳遞過來的。在這個(gè)方法中可以對(duì)UI進(jìn)行更新操作。
  • onPostExecute(Result)(主線程中執(zhí)行):當(dāng)doInBackground(Params...)執(zhí)行完畢并通過return語(yǔ)句進(jìn)行返回時(shí),這個(gè)方法就很快會(huì)被調(diào)用。返回的數(shù)據(jù)會(huì)作為參數(shù)傳遞到這個(gè)方法里面。利用參數(shù)的數(shù)據(jù),就可以進(jìn)行UI的修改。

上面幾個(gè)方法的調(diào)用順序:onPreExcute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExcute()
如果不需要執(zhí)行更新UI操作,順序是:onPreExcute() --> doInBackground() --> onPostExcute()。

  • onCancelled()(主線程中執(zhí)行):該方法被調(diào)用,onPostExecute()方法將不會(huì)被執(zhí)行,需要注意的是,AsncTask中的cancel()不是真正的取消任務(wù),只是將任務(wù)標(biāo)記為取消狀態(tài),我們需要在doInBackground()內(nèi)判斷終止任務(wù)。就好比想要終止一個(gè)線程,調(diào)用interrupt()方法,只是進(jìn)行標(biāo)記為中斷,需要在線程內(nèi)部進(jìn)行標(biāo)記判斷,然后中斷線程。
1.3 AsyncTask的簡(jiǎn)單使用
public class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }
    @Override
    protected Boolean doInBackground(Void... voids) {
        try {
            while (true) {
                int downloadPercent = doDownload();
                publishProgress(downloadPercent);
                if (downloadPercent >= 100) {
                    break;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }
    @Override
    protected void onProgressUpdate(Integer... values) {
        progressDialog.setMessage("當(dāng)前下載進(jìn)度: " + values[0] + "%");
    }
    @Override
    protected void onPostExecute(Boolean result) {
        progressDialog.dismiss();
        if (result) {
            Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();
        }
    }
}

這里模擬了一個(gè)下載任務(wù),在doInBackground方法中,去執(zhí)行具體的下載邏輯,在onProgressUpdate()方法中顯示下載的進(jìn)度。在onPostExecute()方法中提示下載任務(wù)的結(jié)果。
調(diào)用方法:

  new DownloadTask().execute();
1.4 使用AsyncTask的注意事項(xiàng)
    1. 異步任務(wù)的實(shí)例必須在UI線程中創(chuàng)建,即AsynacTask對(duì)象必須在UI線程中創(chuàng)建。
    1. execute(Params... params)必須在UI線程中調(diào)用。
    1. 不要手動(dòng)調(diào)用onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()這幾個(gè)方法。
    1. 不能在doInBackground()中更改UI組件的信息。
    1. 一個(gè)任務(wù)實(shí)例只能執(zhí)行一次,如果執(zhí)行第二次會(huì)拋出異常。

2.AsyncTask的源碼分析

AsyncTask的構(gòu)造函數(shù):

 public AsyncTask() {   this((Looper) null);    }

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

在構(gòu)造函數(shù)中,主要是初始化了3個(gè)成員變量, mHandler,mWorker,mFuture。并在初始化mFutrue的時(shí)候,mWorker作為參數(shù)傳入。 mWorker是一個(gè)Callable對(duì)象,mFuture是以一個(gè)FutureTask對(duì)象。FutureTask實(shí)現(xiàn)了Runnable接口。
mWorker中的call()方法執(zhí)行了耗時(shí)操作,即result = doInBackground(mParams);,然后把得到的結(jié)果通過postResult(result);傳遞給內(nèi)部的Handler跳轉(zhuǎn)到主線程中。這里實(shí)際是在實(shí)例化變量,并沒有開啟執(zhí)行任務(wù)。
postResult()方法源碼:

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

mFuture對(duì)象被加載線程池中,并加載的過程。
new DownloadTask().execute();其中execute()就是執(zhí)行了這一操作。
execute()源碼:

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) {
        mStatus = Status.RUNNING;
        onPreExecute();
        mWorker.mParams = params;
        exec.execute(mFuture);
        return this;
    }

可以看出,execute()方法,實(shí)際上是執(zhí)行了executeOnExecutor()方法,先執(zhí)行了onPreExecute(),然后具體執(zhí)行耗時(shí)任務(wù)是 exec.execute(mFuture);。對(duì)象exec是sDefaultExecutor,往上追溯就是SerialExecutor的實(shí)例化對(duì)象。

    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 是個(gè)內(nèi)部靜態(tài)內(nèi)部類,是所有實(shí)例化的AsyncTask對(duì)象共有的SerialExecutor,內(nèi)部維護(hù)了一個(gè)隊(duì)列,通過鎖使得保證AsyncTask中的任務(wù)是串行執(zhí)行的,即多個(gè)任務(wù)需要一個(gè)個(gè)加到該隊(duì)列中。
這個(gè)方法中主要有兩個(gè)步驟。

    1. 向隊(duì)列中加入一個(gè)新的任務(wù),即之前實(shí)例化的mFuture對(duì)象。
  • 2.調(diào)用scheduleNext(),調(diào)用THREAD_POOL_EXECUTOR執(zhí)行隊(duì)列頭部任務(wù)。
    由此可以知道SerialExecutor僅僅是為了保持任務(wù)執(zhí)行隊(duì)列是串行的實(shí)際執(zhí)行交給了THREAD_POOL_EXECUTOR。

THREAD_POOL_EXECUTOR源碼:

 public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), sThreadFactory);
        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

實(shí)際上就是個(gè)線程池,開啟了一定數(shù)量的核心線程和工作線程。然后調(diào)用線程池的execute()方法,執(zhí)行具體的耗時(shí)任務(wù),即開頭構(gòu)造函數(shù)mWorker中的call()方法。先執(zhí)行doInBackground()方法,然后再執(zhí)行postResult()方法。

在postResult()方法中,向在AsyncTask構(gòu)造函數(shù)中初始化的mHandler發(fā)送了一個(gè)消息。
在構(gòu)造函數(shù)中通過調(diào)用getMainHandler(),實(shí)例化mHandler的源碼:

   private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

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

InternalHandler中,

  • 如果收到的消息是MESSAGE_POST_PROGRESS, 回調(diào)onProgressUpdate()方法, 更新進(jìn)度。
  • 如果收到的消息是MESSAGE_POST_RESULT,即執(zhí)行完了doInBackground()方法并傳遞結(jié)果,那就調(diào)用AsyncTask.finish()方法。
   private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

如果任務(wù)已經(jīng)取消了,就回調(diào)onCancelled()方法,否則就回調(diào)onPostExecute()方法。

InternalHandler是一個(gè)靜態(tài)類, 為了能夠?qū)?zhí)行環(huán)境切換到主線程, 因此這個(gè)類必須在主線程中進(jìn)行加載。 所以變相要求AsyncTask的類必須在主線程中進(jìn)行加載。到此為止, 從任務(wù)執(zhí)行的開始到結(jié)束都從源碼分析完了。

AsyncTask的串行和并行:
默認(rèn)情況下因?yàn)橛?code>SerialExecutor類來維持保證隊(duì)列的串行。如果想要使用并行執(zhí)行任務(wù),那么可以直接跳過SerialExecutor類,使用executeOnExecutor()來執(zhí)行任務(wù)。

3. AsyncTask使用不當(dāng)?shù)暮蠊?/h3>
  • 1)生命周期
    AsyncTask不與任何組件綁定生命周期,所以在Activity或Fragment中創(chuàng)建執(zhí)行AsyncTask時(shí)候,最好在Activity/Fragment的onDestroy()方法中調(diào)用cancle(boolean)方法。
  • 2)內(nèi)存泄漏
    如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會(huì)保留一個(gè)對(duì)創(chuàng)建了AsyncTask的Activity引用。如果Activity已經(jīng)被銷毀,AsyncTask的后臺(tái)線程還在執(zhí)行,它將繼續(xù)在內(nèi)存中保存這個(gè)引用,導(dǎo)致Activity無法被回收,導(dǎo)致內(nèi)存泄漏。
  • 3)結(jié)構(gòu)丟失
    屏幕旋轉(zhuǎn)或者Activity在后臺(tái)被系統(tǒng)殺死等情況會(huì)導(dǎo)致Activity的重新創(chuàng)建,之前運(yùn)行的AsyncTask(非內(nèi)部靜態(tài)類)會(huì)持有之前的Activity的引用,這個(gè)引用已經(jīng)無效,這時(shí)調(diào)用onPostExecute()再去更新界面將不會(huì)再生效。

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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