一.什么是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)是在主線程加載。因此AsyncTask的Handler用的也是主線程的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í)行的嗎?
- 在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)生異常 ,記得在Activity的onDestroy方法中調(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)
