AsyncTask中cancel方法的誤讀
你是否遇到過使用AsyncTask做下載邏輯時,在下載進行到一半點選返回鍵,然後再次回到下載介面時,執行緒並未立即執行,而是過一段時間之後才開始。為了究其原因,我寫了個Demo進行了下驗證。
Demo很簡單,主要就是展示一個進度條,在doInBackground
中用一個for迴圈來定時傳送publishProgress(i)
,在onProgressUpdate
中接受傳遞過來的i,然後讓進度條進行顯示。
執行後你會發現進度條會按照既定設定進行百分比顯示,但是後來你就發現,一旦你在顯示過程中點選返回鍵退出程式,然後再次進入程式,你會發現進度條並未立即顯示,而是等了一段時間後才開始執行。原因就是AsyncTask維護了一個執行緒池,你點選返回的時候並未結束當前執行緒的執行,所以你再次開啟的時候,系統會先將上一次的執行緒結束之後,再開始新的執行緒。
解決方案,我們需要在退出時候給執行緒傳送一個執行緒終止的操作,讓當前執行緒停下來,這樣才能在下次開始時候立即執行新執行緒。這就有用到了生命週期的相關概念了,具體可以參考我的另一篇文章:專案中回退Fragment導致介面重新整理的猜想。我們在onPause中進行停止執行緒操作,程式碼如下:
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
if(myTask != null && myTask.getStatus() == AsyncTask.Status.RUNNING){
myTask.cancel(true );
}
}
之後再次執行程式,咦,發現問題依舊,原因是cancel()
方法並非是直接停止當前執行緒,而是傳送一個停止執行緒的狀態位,也就是說將執行緒池中的某個控制狀態的標誌位變為CANCEL狀態。於是我們還需要繼續進行編碼,在傳送進度的時候判斷下執行緒是否取消狀態,即isCancelled()
,如果是,break或者return出去。
OK了,再次執行程式,看下是否可以立即執行了?
小結下:寫本篇文章的目的就是讓大家知道並非cancel方法就能立即終止執行緒,並非所有的方法是見名知意,這個以後得多加註意。另外就是我發現好多涉及到執行緒的程式碼,只是寫了執行緒執行時候的各種操作,而不去考慮執行緒何時終止,終止後有何操作的邏輯,會無故引發很多的bug。