Android AsyncTask源碼分析

簡介

Android中只能在主線程中進行UI操做,若是是其它子線程,須要藉助異步消息處理機制Handler。除此以外,還有個很是方便的AsyncTask類,這個類內部封裝了Handler和線程池。本文先簡要介紹AsyncTask的用法,而後分析具體實現。android

基本用法

AsyncTask是一個抽象類,咱們須要建立子類去繼承它,而且重寫一些方法。AsyncTask接受三個泛型參數:多線程

  • Params: 指定傳給任務執行時的參數的類型
  • Progress: 指定後臺任務執行時將任務進度返回給UI線程的參數類型
  • Result: 指定任務完成後返回的結果的類型

除了指定泛型參數,還須要根據須要重寫一些方法,經常使用的以下:併發

  • onPreExecute(): 這個方法在UI線程調用,用於在任務執行前作一些初始化操做,如在界面上顯示加載進度控件
  • doInBackground: 在onPreExecute()結束以後馬上在後臺線程調用,用於耗時操做。在這個方法中可調用publishProgress方法返回任務的執行進度
  • onProgressUpdate: 在doInBackground調用publishProgress後被調用,工做在UI線程
  • onPostExecute: 後臺任務結束後被調用,工做在UI線程

源碼分析

下面分析這個類的實現,主要有線程池以及Handler兩部分。異步

線程池

當執行一個AsyncTask的時候調用的是execute()方法,就從這個開始看:ide

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

execute方法會調用executeOnExecutor。在這個方法中先檢查任務是否已經執行或者執行結束,而後把任務標記爲running。最開始執行的是onPreExecute,接着把參數賦值給mWorker對象。這個mWorker是一個Callable對象,最終被包裝爲FutureTask,代碼以下:源碼分析

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
    Params[] mParams;  
} 

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));  
        }  
    };
    
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 occured while executing doInBackground()",  
                    e.getCause());  
        } catch (CancellationException e) {  
            postResultIfNotInvoked(null);  
        }  
    }  
};

從上面的代碼能夠看出,在mWorker對象中的call()方法會調用doInbackground,返回值交給postResult方法,這個方法經過Handler發送消息,這一點稍後再詳細分析。post

mWorker對象被封裝成FutureTask以後交由線程池執行,從execute方法能夠看出,使用的是sDefaultExecutor,它的值默認爲SERIAL_EXECUTOR,也就是串行執行器,實現以下:this

private static class SerialExecutor implements Executor {  
    //線性雙向隊列,用來存儲全部的AsyncTask任務  
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
    //當前正在執行的AsyncTask任務  
    Runnable mActive;  

    public synchronized void execute(final Runnable r) {  
        //將新的AsyncTask任務加入到雙向隊列中  
        mTasks.offer(new Runnable() {  
            public void run() {  
                try {  
                    //執行AsyncTask任務  
                    r.run();  
                } finally {  
                    //當前任務執行結束後執行下一個任務
                    scheduleNext();  
                }  
            }  
        });    
        if (mActive == null) {  
            scheduleNext();  
        }  
    }  

    protected synchronized void scheduleNext() {  
        //從任務隊列中取出隊列頭部的任務,若是有就交給併發線程池去執行  
        if ((mActive = mTasks.poll()) != null) {  
            THREAD_POOL_EXECUTOR.execute(mActive);  
        }  
    }  
}

public static final Executor THREAD_POOL_EXECUTOR  
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

在上面的代碼中,若是有任務執行,那麼SerialExecutorexecute方法會被調用,它的邏輯是把Runnable對象加入ArrayDeque隊列中,而後判斷mActivie是否爲空。第一次執行時mActive固然爲空,因此執行scheduleNext,其實就是取出任務隊列中的第一個任務交給線程池(THREAD_POOL_EXECUTOR)執行。加入mTask隊列的Runnable對象的run方法裏最終必定會調用scheduleNext,那麼又會從任務隊列中取出隊頭任務執行。這樣便實現了單線程順序執行任務,因此在AsyncTask默認啓用的是單線程執行,只有上一個任務執行後纔會執行下一個任務。若是想要啓用多線程執行任務,能夠直接調用 executeOnExecutor(Executor exec, Params... params),這裏的Executor參數可使用AsyncTask自帶的THREAD_POOL_EXECUTOR,也能夠本身定義。.net

Handler

AsyncTask內部用Handler傳遞消息,它的實現以下:線程

private static class InternalHandler extends Handler {  
    @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;  
        }  
    }  
}

若是消息類型是任務執行後的返回值(MESSAGE_POST_RESULT)將調用finish()方法:

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

從上面能夠知道,若是任務取消了,將調用onCancelled,不然調用onPostExecute,因此一個AsyncTask任務若是取消了,那麼onPostExecute將不會獲得執行。

若是消息類型是執行進度(MESSAGE_POST_PROGRESS)將調用onProgressUpdate,這個方法默認是空方法,咱們能夠根據本身的須要重寫。

總結

AsyncTask的主要邏輯就如上面所分析的,總結幾個須要注意的地方:

  • AsyncTask的類必須在UI線程加載(從4.1開始系統會幫咱們自動完成)
  • AsyncTask對象必須在UI線程建立
  • execute方法必須在UI線程調用
  • 不要手動調用onPreExecute()doInBackgroundonProgressUpdate方法
  • 一個任務只能被調用一次(第二次調用會拋出異常)
  • 多個 AsyncTask 默認是串行執行,能夠改成併發執行,但要注意資源同步的問題。
  • 大量 AsyncTask 任務填滿線程池的隊列會拋出異常。

其它還有一些細節能夠自行研究源碼,另外推薦幾篇不錯的文章:

  1. Android源碼分析—帶你認識不同的AsyncTask
  2. Android AsyncTask徹底解析,帶你從源碼的角度完全理解
相關文章
相關標籤/搜索