Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 異步任務:AsyncTask機制 源碼詳細版解析

Android 異步任務:AsyncTask機制 源碼詳細版解析

編輯:關於Android編程

相信大家對AsyncTask機制都不陌生?基本的使用的方法和注意事項都清楚,編寫一個類,繼承AsyncTask類,必須重寫doInBackground()方法,運行在子線程,耗時的操作在此執行;onPostExecute()方法在doInBackground()執行完被回調,運行在主線程,可進行UI更新;若需要顯示更新的進度,可重寫onProgressUpdate()方法。

在一開始學習異步任務AsyncTask時,我們就在被各種教程傳輸這些理論知識,確實對於它已經了然於心,可是有考慮過底層是如何詳細實現AsyncTask機制的嗎?為何onPreExecute()方法最先被調用doInBackground()如何封裝子線程中的?onPostExecute方法回調又是如何實現的?這些方法之間是如何被聯系起來的?

雖然Android已為我們封裝AsyncTask類,只有從源代碼的角度去剖析才能真正掌握。(以下講解會一步步深入,剝繭抽絲,弄清楚詳細實現方式,想要徹底剖析一定要有耐心,另配有邏輯圖供大家整理AsyncTask機制的邏輯流程!)



一. AsyncTask機制

1. AsyncTask基本使用:

AsyncTask asyncTask = new AsyncTask() {

    protected void onPreExecute() {};

    @Override
    protected Object doInBackground(String... params) {
        return null;
    }

    protected void onPostExecute(Object result) {};

    protected void onProgressUpdate(Integer[] values) {};

};
asyncTask.execute("params")

問題:

onPreExecute() 方法為何在主線程中執行?為何最先執行? doInBackground()方法為何在子線程中執行?如何實現該方法執行完就回調 onPostExecute() 方法? onPostExecute() 方法為何在主線程中進行?

以上為AsyncTask的基本使用:new 一個AsyncTask類,重寫需要邏輯實現的方法。調用execute方法,任務就開始執行了。所以,看源代碼就從這個入口execute方法開始分析:



AsyncTask的execute方法

public final AsyncTask execute(Params... params) {
    //(1)檢測狀態
    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)");         
        }
    }
    //(2)修改狀態
    mStatus = Status.RUNNING;

    //(3)調用onPreExecute方法
    onPreExecute();

    //(4)把參數賦值給mWorker對象
    mWorker.mParams = params;

    //(5)線程池對象執行mFuture
    sExecutor.execute(mFuture);

    return this;
}

在此方法體中,開始執行異步任務,逐步驟分析:


(1)檢測狀態

若狀態為RUNNING:顧名思義為正在執行,拋出異常“無法執行,該任務正在執行”。所以說一旦new 了一個AsyncTask連續調用兩次execute方法,肯定會報錯。

若狀態為FINISHED:顧名思義為已經執行過,拋出異常“無法執行,該任務已經執行過,一個任務只能執行一次”。相當於AsyncTask類中的doInBackground方法已經執行完,回調過onPostExecute方法了,再次調用execute方法,比如想再執行一次下載任務,就會報錯!

由以上我們可以得出一個結論:execute方法只能調用一次,若想多次執行,多次的new一個AsyncTask類,再去調用對應的execute方法。



(2)修改狀態

在進過步驟一後(代表狀態判斷未報錯),即可修改狀態為RUNNING(即該任務正在執行)。



(3)調用onPreExecute方法

看到onPreExecute方法是不是覺得倍感親切,雖然我們在new一個AsyncTask重寫其方法時,很少會去重寫該方法。現在我們知道此方法為何是在主線程中執行的了:

因為最開始new一個AsyncTask後,執行execute方法這些步驟本就是在主線程中執行的,而execute方法中又調用onPreExecute方法,所以自然其運行在主線程,並且也是以上四個方法中最先執行的方法!


【AsyncTask機制邏輯流程圖 v1】:
這裡寫圖片描述



(4)把參數賦值給mWorker對象

mWorker.mParams = params;

以上代碼中的params就是 一開始調用AsyncTaskexecute方法時傳進來的參數,此參數被保存到mWorker對象中的mParams變量中,可是這個mWorker又是什麼?首先來看它的聲明

privaet final WorkerRunnable mWorker;

mWorker是一個WorkerRunnable類,繼續深入,查看其具體實現

private static class InternalHandler extends Handler{
    @Override
    public void handleMessage(MMessage msg){
        ...//我也很重要,後面再講
    }

    //☆☆☆看我☆☆☆
    private static abstact class WorkerRunnable implements Callable{
        Params[] mParams;
    }
    ...
}

以上可得知,WorkerRunnable是一個InternalHandler 類(重要的一個類)的內部類,有一個mParams數組,來保存了一開始調用AsyncTaskexecute方法時傳進來的參數


【AsyncTask機制邏輯流程圖 v2】:
這裡寫圖片描述


繼續分析,此類還實現了一個Callable接口,查看具體實現

public interface Callable{
        V call() throws Exception;
}

這個Callable接口中只有一個call()方法,方法作用?何時調用?先留下一個疑問,我們後文分解。既然”mWorker對象的聲明“這一個線索已經挖到底了,就尋找下一個突破口,查看它的初始化

public AsyncTask(){
    //☆☆☆看我☆☆☆
    mWorker = new WorkerRunnable() {
        public Result call() throws Exception {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return doInBackground(mParams);
        }
    };

    mFuture = new FutureTask(mWorker){
        ...//我也很重要,後面再講
    }
}

mWorker初始化部分在AsyncTask的構造方法中,代表最開始new 一個 AsyncTask時,mWorker對象就已經被初始化了。以上分析,其實mWorker就是一個Callable類型對象,它在初始化時new完後,覆蓋了一個call方法,而call方法的返回值正是 調用doInBackground()方法的返回值,並且在調用的時候傳遞了參數mParams


【AsyncTask機制邏輯流程圖 v3】:
這裡寫圖片描述


現在問題二就來了,這個doInBackground()方法是如何運行在子線程中?也就是說這個call方法何時被一個子線程調用?

莫方!先意識到這個問題,保留疑問,繼續往下走。以上就是步驟四的邏輯,我們繼續回到execute方法中,依次執行步驟五,來解決疑問。



(5)線程池對象執行mFuture

sExecutor.execute(mFuture);

老樣子!這一行代碼看不懂,首先查找sExecutor對象聲明

private static final ThreadPool Executor sExecutor = new ThreadPoolsExecutor(CORE_POOL_SIZE,MAXIMUN_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sWorkQueue,sThreadFactory);

真面目揭曉!所以說Asynctask能夠執行多個任務,是因為類中有一個線程池。回到步驟五的那行代碼中,sExecutor對象執行execute方法就是往線程池中取出來一個線程,去處理括號中的內容(即mFuture),mFuture又是何物?來查看聲明

FutureTask mFuture;

得知mFuture是一個FutureTask類型,繼續深入,查看具體實現

public class FutureTask implements RunnableFuture{
    public FutureTask(Callable callable) {
    if (callable == null)
        throw new NullPointerException();
        sync = new Sync(callable);
    }
    ...
}

發現FutureTask類型又是實現了RunnableFuture,剝繭抽絲,繼續深入查看具體實現

public interface RunnableFuture extends Runnable,Future{
    void run();
}

可以恍然大悟了!原來這個RunnableFuture接口是實現了Runnable,所以裡面就有一個run方法,這跟我們所學習的理論知識“運行子線程時一般要在run方法中執行”是非常吻合的!理解之後,這一條“mFuture的聲明”線索也到底了,注意需要明確一點:執行mFuture對象就是在執行run方法!現在問題就是這個run方法在哪裡被調用了?或者說mFuture在哪裡覆蓋重寫run方法?尋找新線索,mFuture聲明完後,來查找它初始化的地方:

public AsyncTask(){
    mWorker = new WorkerRunnable() {
        public Result call() throws Exception {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return doInBackground(mParams);
        }
    };

//☆☆☆看我☆☆☆
    mFuture = new FutureTask(mWorker){
    //此處重寫done方法
    @Override
    protected void done() {
        ...
        //獲取doInbackground方法返回的結果
        result = get();
        ...
        //創建一個消息
        message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(AsyncTask.this, result));
        //把這條消息發送給創建這個消息的Handler:target.sendMessage(this)
        message.sendToTarget();
    }
    };
}

找到mFuture初始化地方,乍一看到步驟四中分析的mWorker對象也是在 AsyncTask的構造方法中(即AsyncTasknew出來的時候這兩個對象就都實例化了!)。並且mFuturenew 一個 FutureTask時,傳了一個參數mWorker。(這裡簡單回顧mWorker:是一個Callable對象,有一個call方法,方法中調用doInBackground),代表將參數mWorker存儲FutureTask類的構造方法中了,繼續深入,查看FutureTask實現

public class FutureTask implements RunnableFuture{
    private final Sync sync;

    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
            sync = new Sync(callable);
    }
} 

查看FutureTask構造方法,也可以證實之前對mWorker對象的回顧,就是一個Callable類型。sync = new Sync(callable);又把mWorker封裝Sync對象Sync為何物?查看其具體實現


Sync(Callable callable) {
    //這裡的callable就是mWorker
    this.callable = callable;
}

只看Sync構造方法即可,可得知只是做了簡單的保存。這條線索“FutureTask類的聲明和實現”也挖到底了,


【AsyncTask機制邏輯流程圖 v4】:
這裡寫圖片描述


回到mFuture對象初始化時(在Asynctask構造方法中),它在new 了一個FutureTask類中,覆蓋重寫了父類的done方法,先暫緩done方法具體實現的分析。

我們之前說過“執行mFuture對象就是在執行run方法”!那麼現在查找run方法在何處被調用

//FutureTask類中的方法
//mFuture的run方法被調用了
public void run() {
    sync.innerRun();
}

run方法中又調用Sync對象innerRun方法,深入,查看其具體實現:

void innerRun() {
    ...
        //調用mWorker的call()
        result = callable.call();
        ...
        set(result);
    ...
}

innerRun方法中,調用了callable(也就是mWorker)的call方法,因為在syncnew出來的時候,在構造方法中就已經把mWorker賦值給了callable,所以實際上是調用mWorker的call方法!所以一大懸案—–問題二“為何doInBackground方法運行在子線程”已被偵破:

mFuturerun方法肯定執行在子線程中,run方法又調用innerRun方法,其也在子線程中,innerRun方法又調用了mWorkercall方法!其call方法中又調用了 doInBackground方法,所以它必然執行在子線程!(這一系列的推理一定要掌握)


【AsyncTask機制邏輯流程圖 v5】:
這裡寫圖片描述


理解之後,新的問題又來了,doInBackground方法如何將其返回值傳給onPostExecute方法中,它是如何被回調的?怎麼又在主線程中運行的?莫方!回到上面代碼繼續往下:
result = callable.call();
調用mWorkercall方法後有一個返回值result
set(result);
又調用了set方法(方法在Sync對象的innerRun方法裡面),將其保存起來。查看set方法具體實現

//FutureTask類中的方法
protected void set(V v) {
     sync.innerSet(v);
}

set方法(在FutureTask類中)中又調用Sync對象的innerSet方法,繼續深入查看:

void innerSet(V v){
    ...
    ...
     if (compareAndSetState(s, RAN)) {
                result = v;
                releaseShared(0);
                //關鍵的done方法
                done();
                return;
      }
  }

innerSet方法中調用了done方法,怎麼有點眼熟?!查看其實現:

//FutureTask類中的方法
protected void done(){}

done方法(在FutureTask類中)沒有方法體,那調用此方法有何意義?但存在即合理!這個方法肯定是在new 一個FutureTask類時,將其覆蓋重寫了!這條線索“執行mFuture對象就是在執行run方法”已完畢,帶著我們的猜測回到mFuture初始化AsyncTask構造方法)代碼中,上面有代碼,再次粘貼主要部分:

mFuture = new FutureTask(mWorker){
    //此處重寫done方法
    @Override
    protected void done() {
        ...
        //獲取doInbackground方法返回的結果
        result = get();
        ...
        //創建一個消息
        message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(AsyncTask.this, result));
        //把這條消息發送給創建這個消息的Handler:target.sendMessage(this)
        message.sendToTarget();
    }
    };

首先是get方法,之前在FutureTask中,Sync變量保存了doInBackground返回值!所以這裡取出結果,取出來後創建了一個消息,傳入了兩個參數:MESSAGE_POST_RESULT(是post的一個結果)、new了一個AsyncTaskresult類,並將doInBackground返回值傳入到newAsyncTaskresult的參數中。


【AsyncTask機制邏輯流程圖 v6】:
這裡寫圖片描述


其實創建消息時接受的兩個參數,一個是post結果,一個是Message裡面的Object,用Object保存了一個AsyncTaskresult對象,這個對象就保存有返回值。最後調用messagesendToTarget方法,這個方法感覺很陌生,繼續深入,查找其實現:

public void sendToTarget(){
    target.sendMessage(this);
}

target調用了sendMessage方法,其實這個target就是一個hanlder,來發送消息,由於sendToTarget方法在Message內部,所以傳入參數this,就是自身。一發消息就會發到Handler中,查找Handler何處聲明

private static final InternalHandler sHandler =  new InternalHandler();

Handler在這裡new 出來了,是一個InternalHandler 類,裡面一定有一個hanlderMessage方法來處理接收的消息:

private static class InternalHandler extends Handler{
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                //調用finish方法
                result.mTask.finish(result.mData[0]);
                break;
             ...
             ...
        }
    }
}

果然如此,聲明了一個內部類繼承於Handler重寫了父類的handleMessage方法,所以之前mFuture對象重寫的done**方法**中發送的消息會發送至此!

之前創建消息的時候將doInBackground返回值作為參數傳入到AsyncTaskresult類中(也就是MessageObject類),現在將result取出來,執行了:

result.mTask.finish(result.mData[0]);

這個mTask就是之前創建消息時傳入的第一個參數,也就是AsyncTask本身mData[0]就是doInBackground返回值。再看AsyncTask對象的finish的方法體:

private void finish(Result result) {
 if (isCancelled()) result = null;
    //調用onPostExecute方法,並傳入結果
    onPostExecute(result);
    mStatus = Status.FINISHED;
}

首先看方法體中的參數result就是doInBackground返回值,注意注意!此處就回調了onPostExecute方法,並傳入結果!所以又一大懸案“onPostExecute如何在doInBackground執行完後實行回調並得到結果?”已偵破!

可是最後一大懸案“onPostExecute方法為何運行在主線程?”

是因為通過Handler發送消息實現的!通過InternalHandler發消息,一發就跑到主線程了呀!






二. AsyncTask機制邏輯流程圖【最終版】

這裡寫圖片描述






三. AsyncTask機制歸納

AsyncTask 異步任務, 主要包含兩個關鍵類:

(1)InternalHandler : 消息處理器, 用於處理線程之間消息.

(2)ThreadPoolExecutor : 線程池, 用於處理耗時任務.



歸納:

結合源代碼分析之後,我們會發現主要是InternalHandlerThreadPoolExecutor這兩個類起著重要作用!

所以,Android 原生的 AsyncTask機制 就是對線程池(也就是ThreadPoolExecutor)的一個封裝,使用其自定義Executor來調度線程的執行方式並發還是串行),最後使用 Handler(也就是InternalHandler) 來完成子線程主線程數據共享



其實寫此博客之前,我搜到網上已經有很多大牛分析過了,並且更加精髓。我在考慮自己再寫會不會有點炒冷飯的感覺,可是別人的東西畢竟是別人的,多總結歸納總是有好處的,共勉~

最後!我討厭csdn博客保存功能,因為之前沒保存,重寫了兩次,心碎!但是當我重寫第二遍的時候,有些細節部分更明白了,所謂溫故而知新啊,也許第一遍看源代碼分析不太懂,但是多看幾遍就理解了!而且此篇博客關鍵在於一步步分析,十分詳細!有何問題歡迎討論。

希望對你們有幫助:)

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved