AsyncTask可能有的坑-Android

desaco發表於2016-02-27

  通過查閱官方文件發現,AsyncTask首次引入時,非同步任務是在一個獨立的執行緒中順序地執行,也就是說一次只能執行一個任務,不能並行地執行,從1.6開始,AsyncTask中引入了執行緒池,支援同時執行5個非同步任務,也就是說同時只能有5個執行緒執行,超過的執行緒只能等待,等待前面的執行緒某個執行完了才被排程和執行。換句話說,如果一個程式中的AsyncTask例項個數超過5個,那麼假如前5個都執行很長時間的話,那麼第6個只能等待機會了。這是AsyncTask的一個限制,而且對於2.3以前的版本無法解決。如果你的應用需要大量的後臺執行緒去執行任務,那麼你只能放棄使用AsyncTask,自己建立執行緒池來管理Thread,或者乾脆不用執行緒池直接使用Thread也無妨。不得不說,雖然AsyncTask較Thread使用起來比較方便,但是它最多隻能同時執行5個執行緒,這也大大侷限了它的實力,你必須要小心的設計你的應用,錯開使用AsyncTask的時間,盡力做到分時,或者保證數量不會大於5個,否則就可能遇到上面提到的問題。可能是Google意識到了AsyncTask的侷限性了,從Android 3.0開始對AsyncTask的API做出了一些調整:每次只啟動一個執行緒執行一個任務,完成之後再執行第二個任務,也就是相當於只有一個後臺執行緒在執行所提交的任務,可以通過程式碼在不同sdk版本中執行的具體情況來驗證官方文件的說法.

  習慣了參照官方文件和執行緒的程式碼,沒有認真研讀原始碼導致踩了AsyncTask的這個坑,如果知道使用executeOnExecutor方法,自己定義執行緒池就不會出現Task任務沒有立即執行的情況,這再次印證了閱讀android原始碼的重要性,最後具體的解決方式如下:

LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService exec = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, blockingQueue);
new LoadTask().executeOnExecutor(1);

》使用Android 4.1之前的版本,使用AsyncTask前,先在UI執行緒中呼叫一次AsyncTask.init()方法;

Android有一個原則---單執行緒模型的原則:UI操作並不是執行緒安全的並且這些操作必須在UI執行緒中執行。

在單執行緒模型中始終要記住兩條法則:
1. 不要阻塞UI執行緒
2. 確保只在UI執行緒中訪問Android UI工具包

》為了正確的使用AsyncTask類,以下是幾條必須遵守的準則:
1) Task的例項必須在UI thread中建立
2) execute方法必須在UI thread中呼叫
3) 不要手動的呼叫onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
4) 該task只能被執行一次,否則多次呼叫時將會出現異常
doInBackground方法和onPostExecute的引數必須對應,這兩個引數在AsyncTask宣告的泛型引數列表中指定,第一個為doInBackground 接 受的引數,第二個為顯示進度的引數,第第三個為doInBackground返回和onPostExecute傳入的引數。

  以上四點是我從網摘過來的,我覺得說的有道理,針對第4點,我有異議:即使多次呼叫,也不應該出現異常,因為AsyncTask類有對外公開的介面,cancel(true),isCancelled()。這兩個方法,這兩個方法配合使用就來控制AsyncTask可以手動退出。

再來說說AsyncTask坑人的地方,就是在Android3.0以後的版本,AsyncTask的執行方法分為2個了:

(1)execute() (2)executeOnExecutor()

如果用AsyncTask呼叫(1)的時候,就表示序列執行執行緒,如果這個Activity中有4個fragment,而且每個fragment都有一個AsyncTask,這樣的話用(1)的話,就必須順序執行,等一個執行完,第二個才執行。如果用方法(2),則可以序列執行,這個UI效果就很好了。執行緒池可以用系統的,也可以用我們自定義的執行緒池;

另外對系統預設執行緒池中執行執行緒數的一些說明,如下:

下面的5代表corePoolSize,10代表阻塞佇列的長度,128代表maximumPoolSize 1:如果執行緒池的數量小於5,則建立新的執行緒並執行 2:如果執行緒數大於5且小於5+10(阻塞佇列大小),則將第6~15的執行緒加入阻塞佇列,待執行緒池中的5個正在執行的執行緒有某個結束後,取出阻塞佇列的執行緒執行。 3:如果執行緒數為16~128,則執行的執行緒數為num-10 4:如果執行緒數大於128,則捨棄。

相關文章