AsyncTask是一個很經常使用的API,尤爲異步處理數據並將數據應用到視圖的操做場合。其實AsyncTask並非那麼好,甚至有些糟糕。本文我會講AsyncTask會引發哪些問題,如何修復這些問題,而且關於AsyncTask的一些替代方案。android
從Android API 3(1.5 Cupcake)開始,AsyncTask被引入用來幫助開發者更簡單地管理線程。實際上在Android 1.0和1.1也是有相似的實現,那就是UserTask。UserTask和AsyncTask有着相同的API及實現,可是因爲因爲1.0和1.1的設備份額微乎其微,這裏的概念就不會涉及到UserTask。併發
關於AsyncTask存在一個這樣普遍的誤解,不少人認爲一個在Activity中的AsyncTask會隨着Activity的銷燬而銷燬。而後事實並不是如此。AsyncTask會一直執行doInBackground()方法直到方法執行結束。一旦上述方法結束,會依據狀況進行不一樣的操做。異步
若是咱們的AsyncTask沒有在Activity銷燬時取消,這會致使AsyncTask崩潰,由於在onPostExecute(Result)方法中處理的視圖已經再也不存在。ui
AsyncTask的cancel方法須要一個布爾值的參數,參數名爲mayInterruptIfRunning,意思是若是正在執行是否能夠打斷
,若是這個值設置爲true,表示這個任務能夠被打斷,不然,正在執行的程序會繼續執行直到完成。若是在doInBackground()方法中有一個循環操做,咱們應該在循環中使用isCancelled()來判斷,若是返回爲true,咱們應該避免執行後續無用的循環操做。this
總之,咱們使用AsyncTask須要確保AsyncTask正確地取消。線程
若是你調用了AsyncTask的cancel(false),doInBackground()仍然會執行到方法結束,只是不會去調用onPostExecute()方法。可是實際上這是讓應用程序執行了沒有意義的操做。那麼是否是咱們調用cancel(true)前面的問題就能解決呢?並不是如此。若是mayInterruptIfRunning設置爲true,會使任務儘早結束,可是若是的doInBackground()有不可打斷的方法會失效,好比這個BitmapFactory.decodeStream() IO操做。可是你能夠提早關閉IO流並捕獲這樣操做拋出的異常。可是這樣會使得cancel()方法沒有任何意義。code
還有一種常見的狀況就是,在Activity中使用非靜態匿名內部AsyncTask類,因爲Java內部類的特色,AsyncTask內部類會持有外部類的隱式引用。詳細請參考細話Java:」失效」的private修飾符,因爲AsyncTask的生命週期可能比Activity的長,當Activity進行銷燬AsyncTask還在執行時,因爲AsyncTask持有Activity的引用,致使Activity對象沒法回收,進而產生內存泄露。對象
另外一個問題就是在屏幕旋轉等形成Activity從新建立時AsyncTask數據丟失的問題。當Activity銷燬並創新建立後,還在運行的AsyncTask會持有一個Activity的非法引用即以前的Activity實例。致使onPostExecute()沒有任何做用。生命週期
關於AsyncTask時串行仍是並行有不少疑問,這很正常,由於它通過屢次的修改。若是你並不明白什麼時串行仍是並行,能夠經過接下來的例子瞭解,假設咱們在一個方法體裏面有以下兩行代碼內存
new AsyncTask1().execute();new AsyncTask2().execute(); |
上面的兩個任務時同時執行呢,仍是AsyncTask1執行結束以後,AsyncTask2才能執行呢?其實是結果依據API不一樣而不一樣。
在初版的AsyncTask,任務是串行調度。一個任務執行完成另外一個才能執行。因爲串行執行任務,使用多個AsyncTask可能會帶來有些問題。因此這並非一個很好的處理異步(尤爲是須要將結果做用於UI試圖)操做的方法。
後來Android團隊決定讓AsyncTask並行來解決1.6以前引發的問題,這個問題是解決了,新的問題又出現了。不少開發者實際上依賴於順序執行的行爲。因而不少併發的問題蜂擁而至。
好吧,開發者可能並不喜歡讓AsyncTask並行,因而Android團隊又把AsyncTask改爲了串行。固然這一次的修改並無徹底禁止AsyncTask並行。你能夠經過設置executeOnExecutor(Executor)來實現多個AsyncTask並行。關於API文檔的描述以下
If we want to make sure we have control over the execution, whether it will run serially or parallel, we can check at runtime with this code to make sure it runs parallel:
public static void execute(AsyncTask as) {if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) {as.execute();} else {as.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);}}//(This code does not work for API lvl 1 to 3) |
並不是如此,使用AsyncTask雖然能夠以簡短的代碼實現異步操做,可是正如本文提到的,你須要讓AsyncTask正常工做的話,須要注意不少條條框框。推薦的一種進行異步操做的技術就是使用Loaders。這個方法從Android 3.0 (Honeycomb)開始引入,在android支持包中也有包含。能夠經過查看官方的文檔來詳細瞭解Loaders。
本次譯文對原文有少部分刪減修改處理。