AsyncTask解析思考

一.什么是AsyncTask?

AsyncTask封裝了線程池和Handler,是一種輕量級(jí)的異步任務(wù)類(lèi),它可以在線程池中執(zhí)行后臺(tái)任務(wù),然后把執(zhí)行的進(jìn)度和最終結(jié)果傳遞給主線程,并在主線程中更新UI。簡(jiǎn)單講就是方便開(kāi)發(fā)者在子線程中更新UI(因?yàn)閮?nèi)部集成了Handler,所以它可以很靈活的在UI線程和子線程之間進(jìn)行切換)

二.AsyncTask應(yīng)該有哪些方法

要做到如上述定義那般,可猜測(cè)它應(yīng)該提供以下幾點(diǎn)內(nèi)容:

1.能定義以下執(zhí)行過(guò)程的操作:
預(yù)執(zhí)行、執(zhí)行后臺(tái)任務(wù)、執(zhí)行進(jìn)度反饋、執(zhí)行完畢

2.能隨時(shí)創(chuàng)建,執(zhí)行,停止線程任務(wù)

3.子線程能與UI線程交互(反饋線程執(zhí)行進(jìn)度和執(zhí)行結(jié)果)

第1點(diǎn),定義這些方法目的是讓調(diào)用者重寫(xiě)而實(shí)現(xiàn)各種交互,因此這四種操作實(shí)際上是四個(gè)抽象方法(或者空方法)AsyncTask類(lèi)中已經(jīng)在合適的時(shí)機(jī)調(diào)用了這四個(gè)抽象方法,顯然,這是模板方法模式(Template Method)的應(yīng)用。

查看AsyncTask源碼,找到上面所提到的對(duì)應(yīng)的四個(gè)方法

protected void onPreExecute() {}   //預(yù)執(zhí)行
protected abstract Result doInBackground(Params... params); //執(zhí)行后臺(tái)任務(wù)
protected void onProgressUpdate(Progress... values) {}//執(zhí)行進(jìn)度反饋
protected void onPostExecute(Result result) {} //執(zhí)行完畢

可以看到,AsyncTask是一個(gè)抽象的泛型類(lèi),需要通過(guò)繼承的方式來(lái)使用。且有三個(gè)方法是有參數(shù)的,正好對(duì)應(yīng)AsyncTask需要指定得三個(gè)三個(gè)泛型參數(shù)的類(lèi)型

public abstract class AsyncTask<Params, Progress, Result>
  • Params:傳遞給后臺(tái)任務(wù)的參數(shù)類(lèi)型。
  • Progress:后臺(tái)任務(wù)執(zhí)行進(jìn)度的類(lèi)型。
  • Result:后臺(tái)任務(wù)執(zhí)行完成返回的結(jié)果類(lèi)型。
    如果AsyncTask確實(shí)不需要傳遞具體的參數(shù),那么這三個(gè)泛型參數(shù)可以用Void來(lái)代替

第2點(diǎn)
能隨時(shí)創(chuàng)建,執(zhí)行,停止線程任務(wù)
對(duì)應(yīng)的方法

          mTask = new DownloadTask(); //創(chuàng)建實(shí)例
          mTask.execute("Hello"); //執(zhí)行
          mTask.cancel(true); //停止
  • 核心方法execute()
    源碼流程太多文章分析了,這里偷懶就簡(jiǎn)單用找來(lái)的一張圖概括了
    AsyncTask.png
  • 取消方法cancel()

AsyncTask 提供了 cancel 方法來(lái)取消,需傳一個(gè) boolean 類(lèi)型的參數(shù)
true 表示會(huì) intercept 中斷 doInBackground,
false 不會(huì)中斷 doInBackground, 它會(huì)完整地執(zhí)行完。
true/false 都不會(huì)再執(zhí)行 onPostResult 方法。

第三點(diǎn)
子線程能與UI線程交互,那想必是用Handler了 源碼中對(duì)應(yīng)InternalHandler

想到好多文章都說(shuō)

AsyncTask 的對(duì)象必須在主線程中創(chuàng)建。

但真的是這樣么,我自己跑了一個(gè)在子線程創(chuàng)建,執(zhí)行任務(wù),是可以的啊,并且也可以更新UI啊,蛤,懷疑人生。各種查資料,好吧了解了AsyncTask跟我一樣命途多舛

三.AsyncTask版本演進(jìn)

1、必須在主線程初始化?

(1)在Android 4.1(API 16)之前,AsyncTask在成員位置直接聲明并創(chuàng)建了Handler對(duì)象,它是一個(gè)靜態(tài)變量,也就是說(shuō)它是在類(lèi)加載的時(shí)候創(chuàng)建的。源碼如下:

private static final InternalHandler sHandler = new InternalHandler();

于是,InternalHandler用了當(dāng)前線程的Looper,此時(shí)需要保證AsyncTask在主線程初始化,從而保證InternalHandler對(duì)應(yīng)的Looper是主線程。

(2)從Android 4.1(API 16)開(kāi)始,到Android 5.1(API 22)之前,在ActivityThread的main函數(shù)里面(App啟動(dòng)的入口),直接調(diào)用了AsyncTask的init()方法,該方法代碼如下:

public static void init() {
    sHandler.getLooper();
}

main函數(shù)運(yùn)行在主線程,這樣就確保了AsyncTask類(lèi)是在主線程加載。因此AsyncTaskHandler用的也是主線程的Looper。但是,不管我們需不需要AsyncTask,它都會(huì)在我們App啟動(dòng)時(shí)就加載,如果我們完全沒(méi)有用到它,很浪費(fèi)資源。

(3)從Android 5.1(API 22)開(kāi)始,在AsyncTask成員變量位置僅僅聲明了靜態(tài)的Handler,并沒(méi)有創(chuàng)建對(duì)象,如下:

private static InternalHandler sHandler; // 還去掉了final關(guān)鍵字

在Handler的實(shí)現(xiàn)中,添加了一個(gè)無(wú)參構(gòu)造:

public InternalHandler() {
   super(Looper.getMainLooper());
}

并添加了getHandler()方法,在需要Handler時(shí)會(huì)調(diào)用此方法。
(注意 InternalHandler的構(gòu)造函數(shù),這家伙把主線程的looper傳給了handler,無(wú)論在哪個(gè)線程創(chuàng)建AsyncTask,最終的回調(diào)還是會(huì)在主線程)

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

這樣既保證了 Handler采用主線程的 Looper 構(gòu)建,又使得 AsyncTask 在需要時(shí)才被加載。

因此,結(jié)論是在Android 4.1(API 16)之前,必須在主線程初始化AsyncTask,創(chuàng)建實(shí)例,從Android 4.1(API 16)開(kāi)始,就不用了。
到此明白了大家為什么說(shuō)AsyncTask的對(duì)象必須在主線程中創(chuàng)建,這句話并不絕對(duì)。
并且還了解了AsyncTask串行,并行的發(fā)展史,不要想當(dāng)然的以為AsyncTask用到了線程池,就肯定是并行的

2、AsyncTask是并行執(zhí)行的嗎?
  1. 在Android 1.6(API 4)之前,AsyncTask是串行執(zhí)行任務(wù)。
    2.在Android 1.6(API 4)到 Android 3.0(API 11)之前,AsyncTask是并行執(zhí)行任務(wù)。
    3.從Android 3.0(API 11),AsyncTask是串行執(zhí)行任務(wù)。如果此時(shí)想執(zhí)行并行任務(wù),可以使用executeOnExecutor方法,如
new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

思考:為什么默認(rèn)不并行?

因?yàn)?AsyncTask 內(nèi)部的線程池是靜態(tài)、單例的,所以在同一進(jìn)程中所有用到 AsyncTask的地方都是同一個(gè)線程池,如果我們創(chuàng)建了多個(gè) AsyncTask且在其中訪問(wèn)了共同資源,并且沒(méi)做同步處理,那么并行時(shí)就會(huì)出現(xiàn)同步問(wèn)題。Google 可能覺(jué)得這很麻煩,為了避免各種問(wèn)題,便在 3.0 以后改成了默認(rèn)串行執(zhí)行。

四.AsyncTask的缺點(diǎn)及注意點(diǎn)

  • 必須在主線程中加載,不然在API 16以下不可用,但目前來(lái)說(shuō),大部分app最低版本也到16了,這個(gè)缺點(diǎn)可以忽略了
  • 內(nèi)存泄露
    Activity中使用非靜態(tài)匿名內(nèi)部AsyncTask類(lèi),會(huì)持有外部類(lèi)的隱式引用。由于AsyncTask的生命周期可能比Activity的長(zhǎng),當(dāng)Activity進(jìn)行銷(xiāo)毀AsyncTask還在執(zhí)行時(shí),由于AsyncTask持有Activity的引用,導(dǎo)致Activity對(duì)象無(wú)法回收,進(jìn)而產(chǎn)生內(nèi)存泄露。改為靜態(tài)內(nèi)部類(lèi)
  • Activity 已被銷(xiāo)毀,doInBackground還沒(méi)有執(zhí)行完,執(zhí)行完后再執(zhí)行 onPostResult, 導(dǎo)致產(chǎn)生異常 ,記得在ActivityonDestroy 方法中調(diào) cancel方法取消 AsyncTask
  • AsyncTask不適合特別耗時(shí)的任務(wù),原因就是上述兩點(diǎn),1.內(nèi)存泄漏 2.生命周期沒(méi)有跟Activity同步,建議用線程池
  • 每個(gè)AsyncTask實(shí)例只能執(zhí)行一次。

參考
再談Android AsyncTask的優(yōu)缺點(diǎn)
AsyncTask的使用方式和版本演進(jìn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容