AsyncTask是一個(gè)很常用的API,尤其異步處理數(shù)據(jù)并將數(shù)據(jù)應(yīng)用到視圖的操作場(chǎng)合。其實(shí)AsyncTask并不是那么好,甚至有些糟糕。本文我會(huì)講AsyncTask會(huì)引起哪些問(wèn)題,如何修復(fù)這些問(wèn)題,并且關(guān)于AsyncTask的一些替代方案。
AsyncTask
從Android API 3(1.5 Cupcake)開(kāi)始,AsyncTask被引入用來(lái)幫助開(kāi)發(fā)者更簡(jiǎn)單地管理線(xiàn)程。實(shí)際上在Android 1.0和1.1也是有類(lèi)似的實(shí)現(xiàn),那就是UserTask。UserTask和AsyncTask有著相同的API及實(shí)現(xiàn),但是由于由于1.0和1.1的設(shè)備份額微乎其微,這里的概念就不會(huì)涉及到UserTask。
生命周期
關(guān)于AsyncTask存在一個(gè)這樣廣泛的誤解,很多人認(rèn)為一個(gè)在Activity中的AsyncTask會(huì)隨著Activity的銷(xiāo)毀而銷(xiāo)毀。然后事實(shí)并非如此。AsyncTask會(huì)一直執(zhí)行doInBackground()方法直到方法執(zhí)行結(jié)束。一旦上述方法結(jié)束,會(huì)依據(jù)情況進(jìn)行不同的操作。
1.如果cancel(boolean)調(diào)用了,則執(zhí)行onCancelled(Result)方法
2.如果cancel(boolean)沒(méi)有調(diào)用,則執(zhí)行onPostExecute(Result)方法
AsyncTask的cancel方法需要一個(gè)布爾值的參數(shù),參數(shù)名為mayInterruptIfRunning,意思是如果正在執(zhí)行是否可以打斷,如果這個(gè)值設(shè)置為true,表示這個(gè)任務(wù)可以被打斷,否則,正在執(zhí)行的程序會(huì)繼續(xù)執(zhí)行直到完成。如果在doInBackground()方法中有一個(gè)循環(huán)操作,我們應(yīng)該在循環(huán)中使用isCancelled()來(lái)判斷,如果返回為true,我們應(yīng)該避免執(zhí)行后續(xù)無(wú)用的循環(huán)操作。
總之,我們使用AsyncTask需要確保AsyncTask正確地取消。
不好好工作的cancel()
簡(jiǎn)而言之的答案,有時(shí)候起作用。
如果你調(diào)用了AsyncTask的cancel(false),doInBackground()仍然會(huì)執(zhí)行到方法結(jié)束,只是不會(huì)去調(diào)用onPostExecute()方法。但是實(shí)際上這是讓?xiě)?yīng)用程序執(zhí)行了沒(méi)有意義的操作。那么是不是我們調(diào)用cancel(true)前面的問(wèn)題就能解決呢?并非如此。如果mayInterruptIfRunning設(shè)置為true,會(huì)使任務(wù)盡早結(jié)束,但是如果的doInBackground()有不可打斷的方法會(huì)失效,比如這個(gè)BitmapFactory.decodeStream() IO操作。但是你可以提前關(guān)閉IO流并捕獲這樣操作拋出的異常。但是這樣會(huì)使得cancel()方法沒(méi)有任何意義。
內(nèi)存泄露
還有一種常見(jiàn)的情況就是,在Activity中使用非靜態(tài)匿名內(nèi)部AsyncTask類(lèi),由于Java內(nèi)部類(lèi)的特點(diǎn),AsyncTask內(nèi)部類(lèi)會(huì)持有外部類(lèi)的隱式引用。詳細(xì)請(qǐng)參考細(xì)話(huà)Java:”失效”的private修飾符,由于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)存泄露。
結(jié)果丟失
另一個(gè)問(wèn)題就是在屏幕旋轉(zhuǎn)等造成Activity重新創(chuàng)建時(shí)AsyncTask數(shù)據(jù)丟失的問(wèn)題。當(dāng)Activity銷(xiāo)毀并創(chuàng)新創(chuàng)建后,還在運(yùn)行的AsyncTask會(huì)持有一個(gè)Activity的非法引用即之前的Activity實(shí)例。導(dǎo)致onPostExecute()沒(méi)有任何作用。
串行還是并行
關(guān)于AsyncTask時(shí)串行還是并行有很多疑問(wèn),這很正常,因?yàn)樗?jīng)過(guò)多次的修改。如果你并不明白什么時(shí)串行還是并行,可以通過(guò)接下來(lái)的例子了解,假設(shè)我們?cè)谝粋€(gè)方法體里面有如下兩行代碼
上面的兩個(gè)任務(wù)時(shí)同時(shí)執(zhí)行呢,還是AsyncTask1執(zhí)行結(jié)束之后,AsyncTask2才能執(zhí)行呢?實(shí)際上是結(jié)果依據(jù)API不同而不同。
在1.6(Donut)之前:
在第一版的AsyncTask,任務(wù)是串行調(diào)度。一個(gè)任務(wù)執(zhí)行完成另一個(gè)才能執(zhí)行。由于串行執(zhí)行任務(wù),使用多個(gè)AsyncTask可能會(huì)帶來(lái)有些問(wèn)題。所以這并不是一個(gè)很好的處理異步(尤其是需要將結(jié)果作用于UI試圖)操作的方法。
從1.6到2.3(Gingerbread)
后來(lái)Android團(tuán)隊(duì)決定讓AsyncTask并行來(lái)解決1.6之前引起的問(wèn)題,這個(gè)問(wèn)題是解決了,新的問(wèn)題又出現(xiàn)了。很多開(kāi)發(fā)者實(shí)際上依賴(lài)于順序執(zhí)行的行為。于是很多并發(fā)的問(wèn)題蜂擁而至。
3.0(Honeycomb)到現(xiàn)在
好吧,開(kāi)發(fā)者可能并不喜歡讓AsyncTask并行,于是Android團(tuán)隊(duì)又把AsyncTask改成了串行。當(dāng)然這一次的修改并沒(méi)有完全禁止AsyncTask并行。你可以通過(guò)設(shè)置executeOnExecutor(Executor)來(lái)實(shí)現(xiàn)多個(gè)AsyncTask并行。關(guān)于API文檔的描述如下
真的需要AsyncTask么
并非如此,使用AsyncTask雖然可以以簡(jiǎn)短的代碼實(shí)現(xiàn)異步操作,但是正如本文提到的,你需要讓AsyncTask正常工作的話(huà),需要注意很多條條框框。推薦的一種進(jìn)行異步操作的技術(shù)就是使用Loaders。這個(gè)方法從Android 3.0 (Honeycomb)開(kāi)始引入,在android支持包中也有包含。可以通過(guò)查看官方的文檔來(lái)詳細(xì)了解Loaders。
本次譯文對(duì)原文有少部分刪減修改處理。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注