AsyncTask總結

AsyncTask是android提供的輕量級的異步類,可以直接繼承AsyncTask,在類中實現異步操作,並提供接口反饋當前異步執行的程度(可以通過接口實現UI進度更新),最後反饋執行的結果給UI主線程。

一、簡介

1.AsyncTask的泛型參數

public abstract class AsyncTask<Params, Progress, Result>

其中,三個泛型類型參數的含義如下:

  • Params:開始異步任務執行時傳入的參數類型;
  • Progress:異步任務執行過程中,返回下載進度值的類型;
  • Result:異步任務執行完成後,返回的結果類型;

如果AsyncTask確定不需要傳遞具體參數,那麼這三個泛型參數可以用Void來代替。

2.AsyncTask的核心方法

onPreExecute()

這個方法會在後臺任務開始執行之間調用,在主線程執行。用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。

doInBackground(Params...)

這個方法中的所有代碼都會在子線程中運行,我們應該在這裏去處理所有的耗時任務。

任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。

onProgressUpdate(Progress...)

當在後臺任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後臺任務中傳遞過來的。在這個方法中可以對UI進行操作,在主線程中進行,利用參數中的數值就可以對界面元素進行相應的更新。

onPostExecute(Result)

當doInBackground(Params...)執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作爲參數傳遞到此方法中,可以利用返回的數據來進行一些UI操作,在主線程中進行,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。

onCancelled()

在主線程中執行,當異步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用,但是要注意的是,AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務爲取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記爲中斷,需要在線程內部進行標記判斷然後中斷線程。

 3.簡單使用

class DownloadTask extends AsyncTask<Integer, Integer, String>{  

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }
    @Override
    protected String doInBackground(Integer... params) {
        for(int i=0;i<=100;i++){
            publishProgress(i);
            try {
                Thread.sleep(params[0]);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "執行完畢";
    }
    @Override
    protected void onProgressUpdate(Integer... progress) {
        //這個函數在doInBackground調用publishProgress時觸發,雖然調用時只有一個參數
        //但是這裏取到的是一個數組,所以要用progesss[0]來取值
        //第n個參數就用progress[n]來取值
        tv.setText(progress[0]+"%");
        super.onProgressUpdate(progress);
    }
    @Override
    protected void onPostExecute(String result) {
        //doInBackground返回時觸發,換句話說,就是doInBackground執行完後觸發
        //這裏的result就是上面doInBackground執行後的返回值,所以這裏是"執行完畢"
        setTitle(result);
        super.onPostExecute(result);
    }
}

需要在主線程啓動

new DownloadTask(1).execute();

二、源碼分析

構造函數

public AsyncTask() {
    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);
            }
        }
    };
}

初始化了兩個變量,mWorker和mFuture,並在初始化mFuture的時候將mWorker作爲參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象。 FutureTask實現了Runnable接口。

mWorker中的call()方法執行了耗時操作,即result = doInBackground(mParams);,然後把執行得到的結果通過postResult(result);,傳遞給內部的Handler跳轉到主線程中。在這裏這是實例化了兩個變量,並沒有開啓執行任務。

execute()方法

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
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;
}

可以 看出,先執行了onPreExecute()方法,然後具體執行耗時任務是在exec.execute(mFuture),把構造函數中實例化的mFuture傳遞進去了。該方法執行過後會把任務的執行狀態賦值爲RUNNING,如果執行任務狀態爲RUNNING的話就會報錯,也就是說一個實例不能執行兩次。

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

SerialExecutor 內部維持了一個隊列,通過鎖使得該隊列保證AsyncTask中的任務是串行執行的,即多個任務需要一個個添加到該隊列中,然後執行完隊列頭部的再執行下一個,以此類推。因爲SerialExecutor 是個靜態內部類,是所有實例化的AsyncTask對象公有的,所以說多個地方提交異步任務執行其實不會並行執行而是串行執行。

在這個方法中,有兩個主要步驟。

  1. 向隊列中加入一個新的任務,即之前實例化後的mFuture對象。
  2. 調用 scheduleNext()方法,調用THREAD_POOL_EXECUTOR執行隊列頭部的任務。

由此可見SerialExecutor 類僅僅爲了保持任務執行是串行的,實際執行交給了THREAD_POOL_EXECUTOR。

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;

看名字也知道這是一個線程池,開啓了一定數量的核心線程和工作線程,並允許核心線程超時回收

postResult()

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:
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

AsyncTask中實例化的Handler是靜態內部類實現的,無論實例化多少AsyncTask對象用的都是一個Handler。並且爲了可以把結果返回到主線程,要求AsyncTask必須在主線程實例化。

在handlerMessage的處理中將消息分爲兩類,一類是結果,走finish()方法,一類是進度,回調onProgressUpdate()

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

在finish()中又分成了兩類,一類是任務取消,回調onCancelled(),一類是任務完成,回調onPostExecute()

三、注意事項

1.生命週期

AsyncTask不與任何組件綁定生命週期,所以在Activity或者Fragment中創建執行AsyncTask時,最好在Activity/Fragment的onDestory()調用 cancel(boolean);

2.內存泄漏

如果AsyncTask被聲明爲Activity的非靜態的內部類,那麼AsyncTask會保留一個對創建了AsyncTask的Activity的引用。如果Activity已經被銷燬,AsyncTask的後臺線程還在執行,它將繼續在內存裏保留這個引用,導致Activity無法被回收,引起內存泄露。

3. 結果丟失

屏幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。

4.AsyncTask並行執行

從Android3.0開始,我們可以通過直接調用AsyncTask方法的executeOnExecutor方法傳入自己定義的線程池,你也可以傳入AsyncTask內部已經定義的THREAD_POOL_EXECUTOR線程池。

四、總結

使用 AsyncTask是創建AsyncTask對象之後執行execute,創建AsyncTask對象的時候會同時創建一個WorkerRunnable 對象,並且以這個WorkerRunnable對象爲參數會創建一個FutureTask對象,那麼分析AsyncTask的原理就該從execute方 法開始了,執行execute方法首先會執行executeOnExecutor方法,並且傳入一個SerialExecutor類型的對 象,SerialExecutor是一個串行線程池,一個線程裏面的所有AsyncTask全部都在這個串行的線程池中排隊執行,在 executeOnExecutor裏面首先會執行onPreExecute方法,該方法是在我們創建AsyncTask對象的時候自己實現的,運行在主 線程中,我們可以在這個方法裏面進行任務開始的提示性操作,接着線程池開始執行,也就是從這一步開始切換到了子線程中,傳入的對象就是我們創建 AsyncTask對象的時候生成的FutureTask對象,在SerialExecutor線程池的execute方法中首先會把當前 FutureTask對象插入到任務隊列中,如果當前任務隊列中沒有正在活動的AsyncTask任務的話,則會執行scheduleNext方法從隊列 中取得一個AsyncTask任務,同時當一個AsyncTask任務執行結束之後會在finally中調用scheduleNext方法執行任務隊列中 的下一個AsyncTask任務,從這裏也看出來默認情況下AsyncTask是串行執行的,真正的執行操作就該在scheduleNext方法裏面 了,這個方法裏面真正執行任務的線程池是THREAD_POOL_EXECUTOR,SerialExecutor線程池是是用來任務排隊的,保證默認情況下的串行執行,而THREAD_POOL_EXECUTOR纔是真正的任務執行者,此外在 AsyncTask裏面還有一個InternalHandler對象,其實他就是一個Handler對象而已,他存在的作用就是爲了從子線程切換到主線程 中,爲了便於在子線程執行的過程中進行一些與界面元素的交互過程,比如下載進度條的更新等等,那麼也就必須要求該InternalHandler對象在主線程中創建了,查看源碼發現InternalHandler對象是static的,也就是在AsyncTask對象創建的時候他就會創建,因此只要保證AsyncTask對象在主線程中創建就可以了,因此使用AsyncTask的時候一定要注意在主線程中創建他的對象,THREAD_POOL_EXECUTOR會執行他的execute方法,該方法實際上執行的是FutureTask的run方法,而 FutureTask的run方法實際上執行的是創建FutureTask對象的時候傳入的參數WorkerRunnable對象的call方法,查看 call方法可以看到執行了doInBackground方法,該方法也是需要我們在創建AsyncTask對象的時候自己實現的,我們可以在這個方法裏 面執行一些比較耗時的操作,它運行在子線程中,在該方法中我們可以通過publishProgress來發送一些耗時任務已經處理的進度信息,該方法運行在子線程中,該方法中會通過InternalHandler將進度消息發送出去,接着在InternalHandler裏面的handleMessage 裏面會發現是通過onProgressUpdate進行消息處理的,該方法運行在主線程中,可以進行更新進度條的一些操作,在 doInBackground方法執行結束後會將返回結果作爲參數傳遞給postResult方法,該方法同樣會通過InternalHandler發送 消息,最後在InternalHandler裏面的handleMessage裏面處理該消息,調用的是finish方法,也就是將線程切換到了主線程中 了,在finish方法中會根據主線程有沒有被暫停來執行onCancelled或者onPostExecute方法,這兩個方法是運行在主線程的,到這 裏AsyncTask的執行結束了;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章