基于最新版本的AsyncTask詳細解讀主要是一些AsyncTask的原理解讀,本篇將分析AsyncTask使用的受限部,這里可能會有一些你以前沒注意到的點,同時提供一個替代方案AsyncTaskScheduler。
一些關于AsyncTask的指責
如果搜索關鍵詞"AsyncTask的缺陷",會有很多關于AsyncTask缺陷的文章,很多是基于以下幾個問題,但這些問題真的是AsyncTask自身的問題還是使用不當導致的呢?這真的需要好好分析一下,不能人云亦云,強行甩鍋。
-
生命周期和內存泄漏
"當Activity結束或者退出應用時AsyncTask會一直執(zhí)行doInBackground()方法直到方法執(zhí)行結束,這可能會導致在onPostExecute時view不存在而導致崩潰潰,以及可能的內存泄露"。
如果退出Activity時AsyncTask扔在執(zhí)行,上面說的的卻會發(fā)生,但這些問題需要由使用者來解決而不是AsyncTask來解決,因為AsyncTask只是執(zhí)行后臺任務,它怎么知道你什么時候要終止,要退出,確定使用Handler時不會出現(xiàn)這樣問題???
怪我嘍???
所以應該在相應的生命周期如onDestory調用cancel取消任務,上面的問題就不會發(fā)生了,所有這個鍋不應該AsyncTask來背。
cancel不能正常取消的問題
首先調用cancel終止AsyncTask的原理是對執(zhí)行異步任務的線程調用interrupt()函數(shù)。
首先,每個線程內部都有一個boolean型變量表示線程的中斷狀態(tài),true代表線程處于中斷狀態(tài),false表示未處于中斷狀態(tài)。
而interrupt()方法的作用只是用來改變線程的中斷狀態(tài)(把線程的中斷狀態(tài)改為true,即被中斷)。因此interrupt()方法代表著外界希望中斷此線程,只是希望,具體怎么處理還是線程內部來做,一般情況下interrupt()方法可以使處于阻塞狀態(tài)的線程拋出InterruptedException從而結束阻塞狀態(tài)或則判斷Thread.interrupted()來處理邏輯。所以如果你的AsyncTask后臺任務有未做中斷的處理肯定會一直執(zhí)行這個線程。所以這需要你自己在doInbackground里進行中斷處理,即使你認為這是個缺陷也應該是Thread類的缺陷,因為要用到線程處理異步任務,AsyncTask無法選擇其他方式,而且調用cancel后onPostExecute也不會在執(zhí)行了,不會導致UI線程的問題,所以這個鍋也不應該AsyncTask來背。Activity意外重啟,狀態(tài)消失問題
比如當用戶旋轉屏幕的時候Activity就會重新啟動,如果之前有AsyncTask正在異步加載處理數(shù)據,那么之前的數(shù)據就會消失,而新的AsyncTask重新創(chuàng)建,這的卻是個問題,但你用其他的方式進行請求同樣會發(fā)生這個問題啊。
其實可能是我們的要求太多了,AsyncTask只是一個處理異步任務的工具,很多邏輯上的東西需要我們自己來處理,就像使用Handler和Thread,不正確處理同樣會出現(xiàn)上述問題,這些總得來說就是異步帶來的問題,這是一個時間和性能的選擇問題,AsyncTask就是簡化了包裝了Handler的處理步驟而已。上面的這些更應該是一些重要的注意事項,而不是AsyncTask的問題
實際存在的問題
- 并行串行問題
上面的一些可能是由于使用不當導致的,但并行串行問題方面AsyncTask問題很大。
使用建議
看AsyncTask源碼文檔時看到這樣建議"AsyncTasks should ideally be used for short operations (a few seconds at the most",就是盡量執(zhí)行一個短時間的任務,最對也就幾秒的任務。當初還很疑惑,AsyncTask這玩意不就是用來處理后臺任務的嗎,又不是在主線程,為什么還限制短時間的任務,那要你何用啊。
Excuse me ? ? ?
基于最新版本的AsyncTask詳細解讀分析過在api11后AsyncTask默認的是串行執(zhí)行任務,基本現(xiàn)上市面上的設備上都將是串行執(zhí)行。自己可以寫個Demo試試看。
這些串行執(zhí)行共用的AsyncTask的是一個線程池,這真的很嚴重。因為是順序執(zhí)行,導致你調用execute() 可能 沒法立刻執(zhí)行,也可能就執(zhí)行不了,因為誰知道有沒有其他的AsyncTask任務在執(zhí)行啊,或者任務還是個很耗時的任務,或者就是個while(ture)循環(huán)或者for(;;)來一直處理一種后臺任務,那么同一進程內的AsyncTask在這之后調用execute的都將無法執(zhí)行。
想當初初學Android時覺得AsyncTask真是個方便的東西,手機寫了個基于socket通信的應用,doInbackground處理,然后直接通知UI。在doInbackground函數(shù)里處理一些連接以及數(shù)據流的接收及發(fā)送,socket的等待連接和數(shù)據結束都是阻塞的啊,現(xiàn)在回想起來當初真是年輕??。
當然你可以立刻執(zhí)行一個任務通過調用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)通過AsyncTask的THREAD_POOL_EXECUTOR線程池或者傳入其他的線程池來立刻執(zhí)行任務,但THREAD_POOL_EXECUTOR有最大并發(fā)數(shù)的限制,具體分析見基于最新版本的AsyncTask詳細解讀,但只執(zhí)行一個任務時通過線程池來管理的也是無奈之舉,而且這種方式不是默認方案啊,不一發(fā)現(xiàn)啊。
錯誤處理問題
AsyncTask沒有對發(fā)生的一些異常進行處理,你只能在onBackground里進行一些判斷,但之外的一些異常情況發(fā)生你都無法了解,比如線程異常退出等。多個任務的管理問題
如果需要多個后臺任務,需要新建多個AsyncTask來執(zhí)行任務,在需要退出的時候你需要對每一個都進行一定的處理來避免內存泄露以及UI問題,這是一個很麻煩的事情。
如果你使用AsyncTask默認的執(zhí)行方式,出了問題都很難排查。你可以保證你能正確使用AsyncTask,但你沒法保證別人也能正確使用啊,這就是別人給你挖的坑,但是你跳了進去啊,關鍵你可能都不知道到底哪個AsyncTask在執(zhí)行,可能引用就發(fā)生在第三方庫的也有可能啊。
替代方案
基于上述實際存在的問題尤其是并行串行問題,寫了一個類似AsyncTask的庫AsyncTaskScheduler,處理了上述的一些實際存在的問題。
細節(jié)介紹及使用