Android AsyncTask詳解(源碼分析)

之前有寫過一篇博客惠遏,關(guān)于Android AsyncTask使用方法 AsyncTask 的使用方法,想著不能又是知其然不知其所以然的狀態(tài)穆端,本篇文章是對 AsyncTask 的深入學(xué)習(xí)。

目錄:

目錄

AsyncTask 是一種輕量級的任務(wù)異步類,可以在后臺子線程執(zhí)行任務(wù)倍权,且將執(zhí)行進度及執(zhí)行結(jié)果傳遞給 UI 線程。

1. AsyncTask 官方文檔介紹

AsyncTask 的官方文檔中有很詳細的介紹捞烟,我將其進行了翻譯薄声,如下所示当船。

1.1 定義

AsyncTask 類讓你很容易去正確使用 UI 線程,它允許你在后臺子線程執(zhí)行異步操作奸柬,然后將執(zhí)行結(jié)果傳遞到UI線程生年,使其做出相對應(yīng)的UI工作,并且這個過程我們不用去操作 Thread 與 Handler廓奕。
AsyncTask 被設(shè)計成一個圍繞 Thread 和 Handler 的助手類抱婉,但并不構(gòu)成一個通用的線程框架。AsyncTasks 最好用于短的耗時操作(最多幾秒鐘)桌粉,對于長時間的耗時處理蒸绩,還是需要通過線程池來處理。
異步任務(wù)由運行在后臺的子線程計算定義铃肯,將其計算結(jié)果發(fā)布到 UI 線程上患亿。異步任務(wù)由3個通用類型(Params、 Progress押逼、Result)和四個執(zhí)行步驟(onPreExecute步藕、 doInBackground、onProgressUpdate挑格、onPostExecute)來定義.

1.2 AsyncTask 的三個泛形參數(shù)

AsyncTask 的三個泛形參數(shù)分別是Params咙冗、Progress、Result漂彤。

  • Params(傳入?yún)?shù)):在執(zhí)行 execute(Params... params) 任務(wù)方法時傳入雾消。
  • Progress(執(zhí)行進度):在后臺計算期間發(fā)布的進度單位。
  • Result(執(zhí)行結(jié)果):后臺的計算結(jié)果挫望。
    這三個參數(shù)不是每次都必須被定義使用立润,如果你不想使用,就定義為 Void媳板。
    如果三個參數(shù)都不使用桑腮,如下所以:
private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    ···
    ···
}

1.3 AsyncTask的4個核心方法

當(dāng) AsyncTask 被執(zhí)行時,會經(jīng)歷4個步驟蛉幸,分別是onPreExecute()到旦、doInBackground()、onProgressUpdate()巨缘、onPostExecute()添忘。
其執(zhí)行步驟分別是:

  1. onPreExecute():在 UI 線程上工作,在任務(wù)執(zhí)行 doInBackground() 之前調(diào)用若锁。此步驟通常用于設(shè)置任務(wù)搁骑,例如在用戶界面中顯示進度條。
  2. doInBackground(Params... params):在子線程中工作,在 onPreExecute() 方法結(jié)束后執(zhí)行仲器,這一步被用于在后臺執(zhí)行長時間的任務(wù)煤率,Params 參數(shù)通過 execute(Params) 方法被傳遞到此方法中。任務(wù)執(zhí)行結(jié)束后乏冀,將結(jié)果傳遞給 onPostExecute(Result) 方法蝶糯,同時我們可以通過 publishProgress(Progress) 方法,將執(zhí)行進度發(fā)送給 onProgressUpdate(Progress) 方法辆沦。
  3. onProgressUpdate(Progress... values):在 UI 線程上工作昼捍,會在 doInBackground() 中調(diào)用 publishProgress(Progress) 方法后執(zhí)行,此方法用于在后臺計算仍在執(zhí)行時(也就是 doInBackgound() 還在執(zhí)行時)將計算執(zhí)行進度通過 UI 顯示出來肢扯。例如妒茬,可以通過動畫進度條或顯示文本字段中的日志,從而方便用戶知道后臺任務(wù)執(zhí)行的進度蔚晨。
  4. onPostExecute(Result result):在 UI 線程上工作乍钻,在任務(wù)執(zhí)行完畢(即 doInBackground(Result) 執(zhí)行完畢)并將執(zhí)行結(jié)果傳過來的時候工作。

1.4 使用方法

由于 AsyncTask 是個抽象類铭腕,所以在使用的時候我們必須使用 AsyncTask 的子類银择,具體實現(xiàn)其方法。子類必須覆蓋重寫 doInBackground 方法累舷,在實際使用中欢摄,通常還會覆蓋重寫 onPostExecute 方法,來處理結(jié)束耗時工作時該做的事情笋粟,通常是 UI 工作。具體代碼可以看另一篇博客Android AsyncTask使用方法析蝴。

1.5 取消任務(wù):

一個任務(wù)可以通過調(diào)用 cancel() 方法在任何時間取消害捕。調(diào)用此方法后再調(diào)用 isCancelled() 方法,返回 true闷畸。調(diào)用此方法后尝盼,onPostExecute() 方法將在 onCancel() 方法返回后調(diào)用,而不是在 doInBackground() 返回結(jié)果后調(diào)用佑菩。為了確保任務(wù)被盡快取消盾沫,你應(yīng)該在 doInBackground() 方法中進行周期性地檢查 isCancelled() 的返回值,如果可能的話(例如在一個循環(huán)中進行檢查)殿漠,比如下面例子:

@Override
protected String doInBackground(String... strings) {
    for (int i = 1; i <= 10; i++) {
        ···
        //在for循環(huán)中進行周期性檢查赴精,檢查異步任務(wù)是否被取消
        if (isCancelled()) {
            break;
        }
        ···
    }

    ···
    return result;
}

1.6 線程規(guī)則:

為了正確的使用 AsyncTask 類, 必須遵循這些線程規(guī)則。

  1. AsyncTask 類必須是在 UI 線程中被加載绞幌,但在Android 4.1(API 16)開始蕾哟,就能被自動加載完成。
  2. AsyncTask 類的實例對象必須在 UI 線程中被創(chuàng)建。
  3. execute() 方法必須是在 UI 線程中被調(diào)用谭确。
  4. 不要手動調(diào)用方法 onPreExecute()帘营、onPostExecute()、doInBackground()逐哈、onProgressUpdate()
  5. 任務(wù)只能執(zhí)行一次(如果嘗試第二次執(zhí)行芬迄,將拋出異常)。

1.7 內(nèi)存可觀測性:

這個翻譯的不好昂秃,如果錯誤禀梳,請指出,虛心請教
AsyncTask 保證所有的回調(diào)調(diào)用都是同步的械蹋。
doInBackground() 可以得知 onPreExecute() 的內(nèi)存影響以及在 execute() 方法調(diào)用之前執(zhí)行的任何其他操作出皇,包括AsyncTask對象的構(gòu)造。
onPostExecute() 可以得知 doInBackground() 方法產(chǎn)生的內(nèi)存影響哗戈。
在執(zhí)行 doInBackground() 方法調(diào)用 publishProgress() 產(chǎn)生的內(nèi)存影響都對其相對應(yīng)的 onProgressUpdate() 方法可見郊艘。但是 doInBackground() 方法繼續(xù)執(zhí)行時,需要注意 doInBackground() 后續(xù)的更新唯咬,不要干擾 onProgressUpdate() 的執(zhí)行纱注。
再調(diào)用 cancel() 方法產(chǎn)生的任何內(nèi)存影響,在調(diào)用 isCancelled() 返回true之后可見胆胰。

1.8 執(zhí)行順序:

在首次引入 AsyncTasks 時狞贱,AsyncTasks 是在單個后臺線程上串行執(zhí)行的。然后從 Android 1.6(API 4) 開始將其更改為允許多個任務(wù)并行操作的線程池蜀涨。
但從 Android 3.0.x(API 11) 開始瞎嬉,任務(wù)在單個線程上執(zhí)行,以避免并行執(zhí)行導(dǎo)致的常見應(yīng)用程序錯誤厚柳。
如果確實需要并行執(zhí)行氧枣,可以調(diào)用線程池

2.源碼分析

我們根據(jù) AsyncTask 的使用步驟進行分析,如果你不熟悉 AsyncTask 的使用方法别垮,請看文章Android AsyncTask使用方法便监,其執(zhí)行步驟如下圖所示。

AsyncTask 使用步驟

2.1 步驟一:創(chuàng)建 AsyncTask 的子類

由于 AsyncTask 是個抽象類碳想,所以我們要創(chuàng)建一個子類烧董,實現(xiàn)它的抽象方法。

public abstract class AsyncTask<Params, Progress, Result> {
    ···
    protected abstract Result doInBackground(Params... params);
    ···
}

2.2 步驟二:實例化 AsyncTask 子類對象

在 UI 線程中實例化一個 AsyncTask 的對象胧奔,即實例化一個我們在步驟一剛剛創(chuàng)建的 AsyncTask 子類的對象逊移。
在 AsyncTask 源碼中包含一個 Handler (InternalHandler)及兩個線程池(SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR),具體代碼見下龙填。
先來看一下 AsyncTask 的構(gòu)造函數(shù)

/**
 * 創(chuàng)建一個新的異步任務(wù)螟左。這個構(gòu)造函數(shù)必須在UI線程上調(diào)用啡浊。
 */
public AsyncTask(@Nullable Looper callbackLooper) {
    //實例化一個 Handler 對象,如果 callbackLooper為空或者是App主線程的Looper胶背,mHandler = getMainHandler();
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);

    //異步任務(wù)巷嚣,是實現(xiàn) Callable 接口的一個對象,可返回結(jié)果钳吟,也可能拋出異常
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            //執(zhí)行任務(wù)廷粒,將 mTaskInvoked 設(shè)置為 true
            mTaskInvoked.set(true);
            //初始化計算結(jié)果 Result。
            Result result = null;
            try {
                //設(shè)置當(dāng)前調(diào)用的線程的優(yōu)先級
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //將參數(shù)傳遞給 doInBackground() 方法红且,并將返回的計算結(jié)果賦值給 result坝茎。
                result = doInBackground(mParams); 
                //將當(dāng)前線程中掛起的任何綁定器命令刷新到內(nèi)核驅(qū)動程序。
                //在執(zhí)行可能會阻塞很長時間的操作之前調(diào)用此方法非常有用暇番,可以確保釋放了所有掛起的對象引用嗤放,以防止進程持有對象的時間超過需要的時間。
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                //異常壁酬,任務(wù)取消次酌,將 mCancelled 設(shè)置為 true。 
                mCancelled.set(true);
                throw tr;
            } finally {
                //任務(wù)執(zhí)行完畢舆乔,將計算結(jié)果result岳服,發(fā)送給InternalHandler
                postResult(result);
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                //任務(wù)正常執(zhí)行
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                //任務(wù)被中斷
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                //任務(wù)被取消
                postResultIfNotInvoked(null);
            }
        }
    };
}

private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            //新建一個 InternalHandler 對象,指定App主線程的消息循環(huán)器希俩。具體見 2.2.1 InternalHandler
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

2.2.1 InternalHandler

InternalHandler 負責(zé)子線程與主線程的溝通吊宋,當(dāng)子線程執(zhí)行完任務(wù)或者被取消任務(wù)后,通知主線程作出相對應(yīng)的 UI 工作颜武。
具體代碼如下:

/**
 * 定義的一個靜態(tài)內(nèi)部類 Handler
 */
private static class InternalHandler extends Handler {
    //looper 為App主線程的消息循環(huán)器
    public InternalHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                //任務(wù)執(zhí)行結(jié)束(包括任務(wù)被取消)璃搜,結(jié)果只有一個,并將該結(jié)果傳遞給 finish() 方法中鳞上。
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                //任務(wù)執(zhí)行中这吻,將執(zhí)行結(jié)果(多個結(jié)果,以數(shù)組的形式保存)傳遞給 onProgressUpdate() 方法中因块。
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

private void finish(Result result) {
    if (isCancelled()) {
        //如果任務(wù)被取消,將計算結(jié)果傳遞給 onCancelled() 方法中籍铁。
        onCancelled(result);
    } else {
        //任務(wù)執(zhí)行完成后涡上,將計算結(jié)果傳遞 onPostExecute() 方法中。
        onPostExecute(result);
    }
    //將當(dāng)前的任務(wù)狀態(tài)設(shè)置為:FINISHED拒名,表示該異步任務(wù)執(zhí)行完畢吩愧。
    mStatus = Status.FINISHED;
}

//如 postResult() 方法,發(fā)送消息給 InternalHandler增显,告知任務(wù)執(zhí)行完畢
private Result postResult(Result result) {
    //將執(zhí)行結(jié)果發(fā)送給 InternalHandler 處理雁佳,根據(jù) MESSAGE_POST_RESULT 標(biāo)識符脐帝,執(zhí)行 finish() 方法。
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

2.2.2 SERIAL_EXECUTOR 線程池

SERIAL_EXECUTOR 線程池糖权,負責(zé)任務(wù)的分發(fā)工作堵腹,按串行順序一次執(zhí)行一個任務(wù)。
具體代碼如下:

/**
 * SERIAL_EXECUTOR 是按串行順序一次執(zhí)行一個任務(wù)的一個線程池星澳。這種序列化對特定進程是全局的疚顷。
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
    //構(gòu)造一個Deque類型的數(shù)組對象(數(shù)組沒有容量限制可,調(diào)整數(shù)組數(shù)量)禁偎,其初始容量為16個元素腿堤。
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        //將 Runnable 任務(wù)插入到 mTasks 末尾位置
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    //執(zhí)行任務(wù)
                    r.run();
                } finally {
                    //當(dāng)前任務(wù)執(zhí)行完后,執(zhí)行下一個任務(wù)
                    scheduleNext();
                }
            }
        });
        //如果 mActive 為空如暖,執(zhí)行下一個任務(wù)
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        //取出 mTasks 隊列頭部元素并賦值給mActive笆檀,如果 mTasks 內(nèi)容為空,返回null
        if ((mActive = mTasks.poll()) != null) {
            //并行執(zhí)行任務(wù)
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

2.2.3 THREAD_POOL_EXECUTOR 線程池

THREAD_POOL_EXECUTOR 線程池盒至,負責(zé)執(zhí)行任務(wù)酗洒,并行執(zhí)行,但其執(zhí)行的任務(wù)是由 SERIAL_EXECUTOR 線程池 所分發(fā)妄迁。
具體代碼如下:

//THREAD_POOL_EXECUTOR 是一個用來并行執(zhí)行任務(wù)的線程池寝蹈。其創(chuàng)建方法如下:

/** 
 * 核心線程數(shù)
 * 我們希望在核心池中至少有2個線程,最多4個線程登淘,希望比cpu計數(shù)少1個箫老,以避免后臺工作使cpu飽和。
 */
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

/** 
 * 返回Java虛擬機可用的CPU處理器數(shù)量黔州。
 */
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/** 
 * 線程池中允許的最大線程數(shù)
 * 最大線程數(shù)為:Java虛擬機可用的CPU處理器數(shù)量 * 2 + 1耍鬓,
 */
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

/** 
 * 非核心線程的存活時間
 */
private static final int KEEP_ALIVE_SECONDS = 30;

/**
 *工作隊列,用于存放被執(zhí)行前的任務(wù)流妻。這個隊列只包含由“execute”方法提交的“Runnable”任務(wù)牲蜀。
 */
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * 線程工廠,用于創(chuàng)建新的線程
 */
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        //請求構(gòu)造一個新的線程绅这,如果請求被拒絕涣达,返回null, 構(gòu)造成功后 mCount 自增 1。
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

/**
 * 可以用來并行執(zhí)行任務(wù)的 Executor 對象证薇。
 */
public static final Executor THREAD_POOL_EXECUTOR;

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

2.3 步驟三:調(diào)用 execute(Params) 啟動任務(wù)

在實例化一個 AsyncTask 的對象后度苔,同樣在 UI 線程上,調(diào)用 execute(Params) 方法浑度,啟動任務(wù)寇窑。

步驟三:調(diào)用 execute(Params) 方法執(zhí)行任務(wù)。
/**
 * 使用指定的參數(shù)執(zhí)行任務(wù)箩张。任務(wù)結(jié)束后返回 AsyncTask 對象甩骏,以便調(diào)用者可以保留對它的引用窗市。
 * 注意:此方法根據(jù)平臺版本將任務(wù)調(diào)度到單個后臺線程或線程池的隊列上。在首次引入異步任務(wù)時饮笛,異步任務(wù)是在單個后臺線程上串行執(zhí)行的咨察。
 * 從 Android 1.6 開始,將其更改為允許多個任務(wù)并行操作的線程池缎浇。然后 Android 3.0 開始扎拣,任務(wù)返回到在一個線程上執(zhí)行,以避免并行執(zhí)行導(dǎo)致的常見應(yīng)用程序錯誤素跺。
 * 如果你真的想要并行執(zhí)行任務(wù)二蓝,你可以使用線程池去操作。
 * 該方法必須在 UI 線程上被調(diào)用指厌。
 *  
 * @param 在UI線程上調(diào)用 execute(Params) 方法傳入的參數(shù)
 * @return AsyncTask的實例話對象
 * @throws 如果 AsyncTask.Status == RUNNING || AsyncTask.Status == FINISHED 時刊愚,拋出 IllegalStateException 異常
 */
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    //調(diào)用 executeOnExecutor() 方法 指定 sDefaultExecutor 線程池來執(zhí)行任務(wù),及傳入 params 參數(shù)踩验。 關(guān)于sDefaultExecutor見 -> 分析1 
    return executeOnExecutor(sDefaultExecutor, params);
}

分析到這一步鸥诽,我們需要知道 sDefaultExecutor 的由來:

//分析:sDefaultExecutor 的由來

//默認將 SERIAL_EXECUTOR 賦值給 sDefaultExecutor,關(guān)于 SERIAL_EXECUTOR 請看 2.2.2 SERIAL_EXECUTOR 線程池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

從上述的代碼中可以發(fā)現(xiàn)箕憾,原來牡借,sDefaultExecutor = SERIAL_EXECUTOR。
SERIAL_EXECUTOR 是按串行順序一次執(zhí)行一個任務(wù)的一個線程池具體請看2.2.2 SERIAL_EXECUTOR 線程池 并且調(diào)用 scheduleNext() 方法袭异,執(zhí)行下一個任務(wù)钠龙,調(diào)用 THREAD_POOL_EXECUTOR.execute() 方法來執(zhí)行任務(wù),那 THREAD_POOL_EXECUTOR 又是什么呢御铃?
THREAD_POOL_EXECUTOR 是用于執(zhí)行任務(wù)的線程池碴里,具體請看 2.2.3 THREAD_POOL_EXECUTOR 線程池

然后我們繼續(xù)往下走,具體看一下 executeOnExecutor(sDefaultExecutor, params) 做了什么上真。

/**
 * 此方法與線程池一起使用咬腋,以允許多個任務(wù)在 AsyncTask 管理的線程池中并行運行。
 * 警告:允許多個任務(wù)在線程池中并行運行通常不是我們想要的睡互,因為它們的操作順序沒有定義根竿。
 * 例如,如果這些任務(wù)用于修改任何公共狀態(tài)(例如單擊按鈕寫入文件)就珠,則不能保證修改的順序寇壳。
 * 在很少的情況下,新版本的數(shù)據(jù)可能被舊版本覆蓋嗓违,從而導(dǎo)致模糊的數(shù)據(jù)丟失和穩(wěn)定性問題九巡。所以最好是串行的順序執(zhí)行這些更改图贸。
 * 該方法在 UI 線程上被調(diào)用
 *
 * @param exec 一個有效便利的進程級別線程池蹂季。
 * @param params 在 UI 線程上調(diào)用 execute(Params) 方法冕广,傳入的參數(shù)。
 * @return AsyncTask 的實例化對象
 * @throws 如果 AsyncTask.Status == RUNNING || AsyncTask.Status == FINISHED 時偿洁,拋出 IllegalStateException 異常
 */
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    //如果任務(wù)已經(jīng)被執(zhí)行
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            //任務(wù)正在執(zhí)行狀態(tài)
            case RUNNING:
                //拋出異常:不能執(zhí)行任務(wù)撒汉,該任務(wù)正在被執(zhí)行。
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            //任務(wù)已被執(zhí)行完畢    
            case FINISHED:
                //拋出異常:不能執(zhí)行任務(wù)涕滋,該任務(wù)已經(jīng)被執(zhí)行完畢睬辐,一個任務(wù)只能被執(zhí)行一次
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }
    //如果任務(wù)還未被執(zhí)行過,則開始執(zhí)行任務(wù)宾肺,將任務(wù)狀態(tài)標(biāo)記為 RUNNING 正在執(zhí)行
    mStatus = Status.RUNNING;
    //執(zhí)行 onPreExecute() 方法溯饵,具體內(nèi)容由我們在 AsyncTask 子類中復(fù)寫定義
    onPreExecute();
    //將傳入的參數(shù)賦值給 mWorker.mParams (為什么給他,他要干什么)
    mWorker.mParams = params;
    //線程池開始執(zhí)行任務(wù)锨用,mFuture 是什么丰刊?
    exec.execute(mFuture);

    return this;
}

走到這一步時,發(fā)現(xiàn)遇到了兩個新對象:mWorker增拥、mFuture啄巧。最終也是由線程池exec.execute(mFuture) 來執(zhí)行任務(wù),那我們就先來看看 mWorker 與 mFuture 吧掌栅。

private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;

/**
 * 實現(xiàn) Callable 的一個抽象函數(shù)秩仆,存儲異步任務(wù)
 */
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

/**
 * FutureTask 是一個可取消異步任務(wù)計算工作的類,在運行期間執(zhí)行可調(diào)用的 Callable 任務(wù)
 * @param  可調(diào)用的任務(wù)
 * @throws 如果 callable 為 null猾封,拋出 NullPointerException 異常澄耍。
 */
public FutureTask(Callable<V> callable) {
    //如果傳入的 Callable 任務(wù)為空,拋出異常忘衍;否則逾苫,等待執(zhí)行該任務(wù)
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;
}

//我們再來看一下 Callable 接口
/**
 * 返回結(jié)果并可能引發(fā)異常的任務(wù)。實現(xiàn)者定義一個沒有參數(shù)的 call() 方法枚钓。
 * Callable 接口類似于 Runnable铅搓,因為它們都是為那些實例可能由另一個線程執(zhí)行的類設(shè)計的。但是 Runnable 不返回結(jié)果搀捷,也不能拋出已檢查的異常星掰。
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 * Executor 類包含將其他常見形式轉(zhuǎn)換為 Callable 類的方法。
 */
@FunctionalInterface
public interface Callable<V> {
    /**
     * 計算結(jié)果嫩舟,如果無法計算氢烘,則拋出異常
     *
     * @return 計算結(jié)果
     * @throws 如果無法計算,則拋出異常
     */
    V call() throws Exception;
}


//其實 mWorker 與 mFuture 在 步驟二:實例化 AsyncTask 子類對象 中已經(jīng)被實例化家厌。
//這邊為了方便大家閱讀播玖,再次展示一下代碼:
public AsyncTask(@Nullable Looper callbackLooper) {
    ···
    //異步任務(wù),是實現(xiàn) Callable 接口的一個對象饭于,可返回結(jié)果蜀踏,也可能拋出異常
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            //執(zhí)行任務(wù)维蒙,將 mTaskInvoked 設(shè)置為 true
            mTaskInvoked.set(true);
            //初始化計算結(jié)果 Result。
            Result result = null;
            try {
                //設(shè)置當(dāng)前調(diào)用的線程的優(yōu)先級
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //將參數(shù)傳遞給 doInBackground() 方法果覆,并將返回的計算結(jié)果賦值給 result颅痊。
                result = doInBackground(mParams); 
                //將當(dāng)前線程中掛起的任何綁定器命令刷新到內(nèi)核驅(qū)動程序。
                //在執(zhí)行可能會阻塞很長時間的操作之前調(diào)用此方法非常有用局待,可以確保釋放了所有掛起的對象引用斑响,以防止進程持有對象的時間超過需要的時間。
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                //異常钳榨,任務(wù)取消舰罚,將 mCancelled 設(shè)置為 true。 
                mCancelled.set(true);
                throw tr;
            } finally {
                //任務(wù)執(zhí)行完畢薛耻,將計算結(jié)果result沸停,發(fā)送給InternalHandler
                postResult(result);
            }
            return result;
        }
    };

    //一個可取消異步任務(wù)計算工作的類,將 mWorker 這個 Callable 對象傳遞給 FutureTask, 其執(zhí)行任務(wù)計算工作
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                //任務(wù)執(zhí)行完成昭卓,傳遞執(zhí)行結(jié)果
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                //任務(wù)被中斷
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                //任務(wù)被取消
                postResultIfNotInvoked(null);
            }
        }
    };
}

當(dāng)任務(wù)執(zhí)行完后愤钾,調(diào)用 postResult(Result) 方法,告知 InternalHandler 來處理執(zhí)行結(jié)果候醒。

//如 postResult() 方法能颁,發(fā)送消息給 InternalHandler,告知任務(wù)執(zhí)行完畢
private Result postResult(Result result) {
    //將執(zhí)行結(jié)果發(fā)送給 InternalHandler 處理倒淫,根據(jù) MESSAGE_POST_RESULT 標(biāo)識符伙菊,執(zhí)行 finish() 方法。
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

關(guān)于 InternalHandler 請看 2.2.1 InternalHandler

現(xiàn)在我們可以總結(jié)一下 AsyncTask 的執(zhí)行流程敌土,我們在 Activity 中 調(diào)用 execute(Params) 方法開始執(zhí)行任務(wù)镜硕,其執(zhí)行順序為:

  1. 在 executeOnExecutor() 方法執(zhí)行 exec.execute(mFuture)
  2. SerialExecutor.execute(final Runnable r),這里的 Runnable r 就是 mFuture
  3. r.run()
  4. FutureTask.run()
  5. callable.call()返干,這里 callable 就是 mWorker 對象
  6. mWorker.call()
  7. doInBackground(mParams)
  8. postResult(result);
  9. InternalHandler.handleMessage()

整理了一下流程圖兴枯,方便更直觀的梳理執(zhí)行流程,如下所示矩欠。(跟著步驟走一遍就有體會了)

AsyncTask 執(zhí)行任務(wù) 流程圖

2.4 額外:顯示任務(wù)執(zhí)行進度

當(dāng)我們在執(zhí)行任務(wù)時财剖,如果我們要處理執(zhí)行進度,則需在 doInBackground() 方法內(nèi)調(diào)用 publishProgress(Progress) 方法癌淮。比如:

@Override
protected String doInBackground(String... strings) {
        ···
        //通過調(diào)用publishProgress()方法躺坟,將執(zhí)行進度傳遞給onProgressUpdate()
        publishProgress(progressValue);
        ···
    }
    ···
}

那 publishProgress(Progress) 方法做了什么呢? 往下看:

/**
 * 這個方法在 doInBackground() 方法中調(diào)用乳蓄,當(dāng)后臺任務(wù)還在執(zhí)行時咪橙,將執(zhí)行進度發(fā)布到 UI 線程。
 * 每次調(diào)用這個方法,都會觸發(fā) UI 線程中的 onProgressUpdate() 方法美侦。
 *
 * 如果任務(wù)被取消則不會觸發(fā) onProgressUpdate() 方法店诗。
 *
 * @param values 執(zhí)行進度,去更新UI
 */
@WorkerThread
protected final void publishProgress(Progress... values) {
    //如果任務(wù)沒有被取消音榜,發(fā)送消息給 InternalHandler,
    //根據(jù) MESSAGE_POST_PROGRESS 標(biāo)識符處理消息捧弃,調(diào)用 onProgressUpdate(Progress) 方法
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}


2.5 額外:取消任務(wù)

在主線程上通過調(diào)用 cancel(true) 方法來取消任務(wù)赠叼。

/**
 * 嘗試取消正在執(zhí)行的任務(wù)。如果任務(wù)已經(jīng)執(zhí)行完畢或者已經(jīng)被取消再或者出于某種原因违霞,那就會導(dǎo)致嘗試取消任務(wù)失敗嘴办。
 * 如果取消任務(wù)成功,當(dāng)任務(wù)還沒有開始執(zhí)行時嘗試取消任務(wù)买鸽,這樣任務(wù)就不會被執(zhí)行涧郊。
 * 如果任務(wù)已經(jīng)啟動,那么 mayInterruptIfRunning 參數(shù)決定執(zhí)行此任務(wù)的線程是否應(yīng)該中斷眼五,以嘗試停止任務(wù)妆艘。
 * 
 * 調(diào)用此方法后會導(dǎo)致 UI 線程在 doInBackground() 方法執(zhí)行結(jié)束返回結(jié)果后調(diào)用 onCancelled() 方法。
 * 調(diào)用這個方法后可以保證以后永遠不會調(diào)用 onPostExecute() 方法看幼,即使 cancel() 方法返回 false, 也不會執(zhí)行 onPostExecute() 方法批旺。
 * 為了盡早的發(fā)現(xiàn)結(jié)束任務(wù),可以在 doInBackground() 方法內(nèi)進行周期性檢查 isCancelled()诵姜。
 * 
 * 這只要求取消任務(wù)汽煮。它從不等待正在運行的后臺任務(wù)終止,即使 mayInterruptIfRunning 為 true棚唆。
 * @param mayInterruptIfRunning 如果為 true暇赤,說明線程執(zhí)行該任務(wù)時應(yīng)該被中斷。否則宵凌,將允許正在進行的任務(wù)完成鞋囊。
 *
 * @return 如果任務(wù)不能被取消(一般是因為任務(wù)已經(jīng)被完成),返回 false瞎惫。否則失暴,返回 true。
 */
public final boolean cancel(boolean mayInterruptIfRunning) {
    //將 mCancelled 屬性設(shè)置為 true.
    mCancelled.set(true);
    //mFuture 執(zhí)行 cancel() 方法微饥。
    return mFuture.cancel(mayInterruptIfRunning);
}

3. 總結(jié)

通過 AsyncTask 的源碼我們可以發(fā)現(xiàn)逗扒,其原理還是 Thread 與 Handler 的實現(xiàn),Android 幫我們封裝了一下欠橘,所以我們可以很方便的使用矩肩。
其包含 兩個線程池,一個 Handler,如下所示:

名稱 類型 作用
SERIAL_EXECUTOR 線程池 分發(fā)任務(wù)黍檩,串行分發(fā)叉袍,一次只分發(fā)一個任務(wù)
THREAD_POOL_EXECUTOR 線程池 執(zhí)行任務(wù),并行執(zhí)行刽酱,執(zhí)行的任務(wù)由 SERIAL_EXECUTOR 分發(fā)
InternalHandler Handler 負責(zé)子線程與主線程的溝通喳逛,通知主線程做 UI 工作

其之間的聯(lián)系概括起來如下圖所示:

兩個線程池與 Handler 之間的關(guān)系圖

到此 AsyncTask 的源碼也就分析結(jié)束了,最好的理解方式就是走一遍

其實分享文章出來的最大目的正是等著有人指出我的錯誤棵里,如果你發(fā)現(xiàn)哪里有錯誤润文,請毫無保留的指出即可,虛心請教殿怜。

另外典蝌,如果你覺得文章不錯,對你有所幫助头谜,請給我點個贊骏掀,謝謝~Peace~!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柱告,一起剝皮案震驚了整個濱河市截驮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌际度,老刑警劉巖侧纯,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甲脏,居然都是意外死亡眶熬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門块请,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娜氏,“玉大人,你說我怎么就攤上這事墩新∶趁郑” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵海渊,是天一觀的道長绵疲。 經(jīng)常有香客問我,道長臣疑,這世上最難降的妖魔是什么盔憨? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮讯沈,結(jié)果婚禮上郁岩,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好问慎,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布萍摊。 她就那樣靜靜地躺著,像睡著了一般如叼。 火紅的嫁衣襯著肌膚如雪冰木。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天笼恰,我揣著相機與錄音踊沸,去河邊找鬼。 笑死挖腰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的练湿。 我是一名探鬼主播猴仑,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼肥哎!你這毒婦竟也來了辽俗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤篡诽,失蹤者是張志新(化名)和其女友劉穎崖飘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杈女,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡朱浴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年旭从,在試婚紗的時候發(fā)現(xiàn)自己被綠了淋袖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姆打。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡根吁,死狀恐怖棒坏,靈堂內(nèi)的尸體忽然破棺而出损搬,到底是詐尸還是另有隱情郭宝,我是刑警寧澤重虑,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布蝇裤,位于F島的核電站廷支,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏栓辜。R本人自食惡果不足惜恋拍,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望藕甩。 院中可真熱鬧芝囤,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至悯许,卻和暖如春仆嗦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背先壕。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工瘩扼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垃僚。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓集绰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谆棺。 傳聞我的和親對象是個殘疾皇子栽燕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容