AsyncTask是一個很常用的API,尤其異步處理數據并將數據應用到視圖的操作場合。其實AsyncTask并不是那么好,甚至有些糟糕。本文我會講AsyncTask會引起哪些問題,如何修復這些問題,并且關于AsyncTask的一些替代方案。
AsyncTask
從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()方法直到方法執行結束。一旦上述方法結束,會依據情況進行不同的操作。
1.如果cancel(boolean)調用了,則執行onCancelled(Result)方法
2.如果cancel(boolean)沒有調用,則執行onPostExecute(Result)方法
AsyncTask的cancel方法需要一個布爾值的參數,參數名為mayInterruptIfRunning,意思是如果正在執行是否可以打斷,如果這個值設置為true,表示這個任務可以被打斷,否則,正在執行的程序會繼續執行直到完成。如果在doInBackground()方法中有一個循環操作,我們應該在循環中使用isCancelled()來判斷,如果返回為true,我們應該避免執行后續無用的循環操作。
總之,我們使用AsyncTask需要確保AsyncTask正確地取消。
不好好工作的cancel()
簡而言之的答案,有時候起作用。
如果你調用了AsyncTask的cancel(false),doInBackground()仍然會執行到方法結束,只是不會去調用onPostExecute()方法。但是實際上這是讓應用程序執行了沒有意義的操作。那么是不是我們調用cancel(true)前面的問題就能解決呢?并非如此。如果mayInterruptIfRunning設置為true,會使任務盡早結束,但是如果的doInBackground()有不可打斷的方法會失效,比如這個BitmapFactory.decodeStream() IO操作。但是你可以提前關閉IO流并捕獲這樣操作拋出的異常。但是這樣會使得cancel()方法沒有任何意義。
內存泄露
還有一種常見的情況就是,在Activity中使用非靜態匿名內部AsyncTask類,由于Java內部類的特點,AsyncTask內部類會持有外部類的隱式引用。詳細請參考細話Java:”失效”的private修飾符,由于AsyncTask的生命周期可能比Activity的長,當Activity進行銷毀AsyncTask還在執行時,由于AsyncTask持有Activity的引用,導致Activity對象無法回收,進而產生內存泄露。
結果丟失
另一個問題就是在屏幕旋轉等造成Activity重新創建時AsyncTask數據丟失的問題。當Activity銷毀并創新創建后,還在運行的AsyncTask會持有一個Activity的非法引用即之前的Activity實例。導致onPostExecute()沒有任何作用。
串行還是并行
關于AsyncTask時串行還是并行有很多疑問,這很正常,因為它經過多次的修改。如果你并不明白什么時串行還是并行,可以通過接下來的例子了解,假設我們在一個方法體里面有如下兩行代碼
上面的兩個任務時同時執行呢,還是AsyncTask1執行結束之后,AsyncTask2才能執行呢?實際上是結果依據API不同而不同。
在1.6(Donut)之前:
在第一版的AsyncTask,任務是串行調度。一個任務執行完成另一個才能執行。由于串行執行任務,使用多個AsyncTask可能會帶來有些問題。所以這并不是一個很好的處理異步(尤其是需要將結果作用于UI試圖)操作的方法。
從1.6到2.3(Gingerbread)
后來Android團隊決定讓AsyncTask并行來解決1.6之前引起的問題,這個問題是解決了,新的問題又出現了。很多開發者實際上依賴于順序執行的行為。于是很多并發的問題蜂擁而至。
3.0(Honeycomb)到現在
好吧,開發者可能并不喜歡讓AsyncTask并行,于是Android團隊又把AsyncTask改成了串行。當然這一次的修改并沒有完全禁止AsyncTask并行。你可以通過設置executeOnExecutor(Executor)來實現多個AsyncTask并行。關于API文檔的描述如下
真的需要AsyncTask么
并非如此,使用AsyncTask雖然可以以簡短的代碼實現異步操作,但是正如本文提到的,你需要讓AsyncTask正常工作的話,需要注意很多條條框框。推薦的一種進行異步操作的技術就是使用Loaders。這個方法從Android 3.0 (Honeycomb)開始引入,在android支持包中也有包含。可以通過查看官方的文檔來詳細了解Loaders。
本次譯文對原文有少部分刪減修改處理。
新聞熱點
疑難解答
圖片精選