深入理解AsyncTask

1 AsyncTask介紹

Android中有Handler和AsyncTask來實現異步類,Android中UI的更新只能在主線程中完成,爲了不阻塞主線程,耗時的操作需要在異步類中完成。

AsyncTask是Android提供的輕量級的異步類,可以直接繼承AsyncTaask,在類中實現異步操作,並提供結構反饋當前異步執行的程度,簡單快捷,進程可控。

AsyncTask定義了三種泛型類型參數,Params, Progress和Result。
- Params 啓動任務執行的輸入參數,如Http請求的URL
- Progress 後臺任務執行的百分比
- Result 後臺執行任務最終返回的結果

使用AsyncTask來實現異步任務時,至少需要重寫以下兩個方法:
- doInBackground(Params…) 後臺執行的耗時操作,如網絡請求,注意不能在這個方法中更新UI。
- onPostExecute(Result) 這個方法是在UI線程中,主要用來在耗時操作完成後更新UI

還有其他的三個方法,但不是必須要實現的
- onProgressUpdate(Progress) 顯示任務執行的進度
- onPreExecute() 任務執行之前調用此方法,可以在這裏顯示進度對話框
- onCancelled() 用戶取消耗時操作時的操作

使用AsyncTask類實現異步操作時,需要注意一下幾點:
- Task必須在主線程中創建
- execute()方法必須在主線程中調用
- 每個task只能執行一次,多次調用會產生異常

下面是一個AsyncTask使用的例子,注意需要在主線程中調用execute()方法

public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {  

    private TextView textView;  
    private ProgressBar progressBar;  


    public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {  
        super();  
        this.textView = textView;  
        this.progressBar = progressBar;  
    }  


    /**  
     * 這裏的Integer參數對應AsyncTask中的第一個參數   
     * 這裏的String返回值對應AsyncTask的第三個參數  
     * 該方法並不運行在UI線程當中,主要用於異步操作,所有在該方法中不能對UI當中的空間進行設置和修改  
     * 但是可以調用publishProgress方法觸發onProgressUpdate對UI進行操作  
     */  
    @Override  
    protected String doInBackground(Integer... params) {  
        NetOperator netOperator = new NetOperator();  
        int i = 0;  
        for (i = 10; i <= 100; i+=10) {  
            netOperator.operator();  
            publishProgress(i);  
        }  
        return i + params[0].intValue() + "";  
    }  


    /**  
     * 這裏的String參數對應AsyncTask中的第三個參數(也就是接收doInBackground的返回值)  
     * 在doInBackground方法執行結束之後在運行,並且運行在UI線程當中 可以對UI空間進行設置  
     */  
    @Override  
    protected void onPostExecute(String result) {  
        textView.setText("異步操作執行結束" + result);  
    }  


    //該方法運行在UI線程當中,並且運行在UI線程當中 可以對UI空間進行設置  
    @Override  
    protected void onPreExecute() {  
        textView.setText("開始執行異步線程");  
    }  


    /**  
     * 這裏的Intege參數對應AsyncTask中的第二個參數  
     * 在doInBackground方法當中,,每次調用publishProgress方法都會觸發onProgressUpdate執行  
     * onProgressUpdate是在UI線程中執行,所有可以對UI空間進行操作  
     */  
    @Override  
    protected void onProgressUpdate(Integer... values) {  
        int vlaue = values[0];  
        progressBar.setProgress(vlaue);  
    }  
}  

2 AsyncTask代碼解析

我們進入AsyncTask類中查看這個異步類的工作工作流程,當我們在主線程中調用AsyncTask的execute()方法後。

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

執行的是executeOnExecutor()方法,其中一個參數是我我們調用時的params,而sDefaultExecutor我們就不知道是什麼類型了。查課你sDefaultExecutor的定義,是一個SerialExecutor類的對象。SerialExecutor內部類定義如下:

 private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
           ...
        }

        protected synchronized void scheduleNext() {
           ...
        }
    }

SerialExecutor類實現了Executor的接口,主要是execute()方法。我們稍後再介紹。回到前面executeOnExecutor()函數,我們看一下這個函數的定義.

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

首先判斷狀態status, Status是一個枚舉類型,包含三個值:
- PENDING : 待定狀態
- RUNNING : 正在運行狀態,如果執行一個操作,而這個操作的狀態爲正在執行,會拋出異常
- FINISHED : 已完成狀態

如果狀態爲PENDING, 則會繼續執行,首先會把狀態改爲RUNNING,然後回調onPreExecute()方法。接下來我們看一下mWorker的定義。

public AsyncTask() {  
        mWorker = new WorkerRunnable<Params, Result>() {  
            public Result call() throws Exception {  
                mTaskInvoked.set(true);  

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
                //noinspection unchecked  
                return postResult(doInBackground(mParams));  
            }  
        };  
//….  

}  

可以看到mWorker在構造方法中完成了初始化,在call方法中最終調用了我們熟悉的doInBackground()方法,並返回Result值作爲參數給postResult方法。

接下來我們分析postResult方法

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

在postResult方法中,首先調用了getHandler()方法,可以猜測這個方法得到了一個實現異步消息機制的Handlder對象,我們查看該函數

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

果然,函數返回一個Handler對象,不過對象是自定義的InternalHandler的。我們繼續查看InternalHandler類的定義

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

當msg.what = MESSAGE_POST_RESULT時,調用了mTask的finish方法,當msg.what = MESSAGE_POST_PROGRESS時,調用了mTask.onProgressUpdate()方法。我們分析一下finish()方法

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

當請求沒有被cancel時,調用了onPostExecute(result)函數,即通過handler,在主線程中改變UI的狀態。最後將狀態改爲已完成。

3 AsyncTask存在的問題

(1) 生命週期

很多人認爲一個在Activity中的AsyncTask會隨着Activity的銷燬而銷燬,然而事實並非如此,AsyncTask會一直執行doInBackground方法直到方法結束。一旦上述方法結束,會根據情況進行不同的操作:
- 如果cancel(boolean)調用了,則執行onCancelled(Resule)方法
- 如果cancel(boolean)沒有調用,則執行onPostExecute(Result)方法

(2)內存泄露

在Activiy中使用非靜態匿名內部AsyncTask類,由於java內部類的特點,AsysncTask內部類會持有外部類的隱式引用。由於AsyncTask的生命週期可能比Activity的長,當Activity銷燬時AsyncTask還在執行,而AsyncTask持有Activity的引用,導致Activity無非回收,進而產生內存泄露。

發佈了21 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章