AsyncTask 坑(一) 哪些執行緒可以呼叫AsyncTask
看到很多文章說,只有主執行緒(UI執行緒)可以建立AsyncTask。個人覺得這個說法不對。從AsyncTask的角度講,任何執行緒都可以建立並且使用AsyncTask。之所以會有主執行緒才能建立AsyncTask的說法,我想無非是onPostExecute, onProgressUpdate等回撥裡面想操作UI吧。假如一個後臺執行緒建立了一個AsyncTask物件,並且使用它,那麼當任務完成後,通過handler會發送訊息過來,而handler是在AsyncTask內部的,那麼就擔心handler會在後臺執行緒裡面跑,然後一處理UI就掛了。
實際上是這樣嗎?
事實勝於雄辯,看程式碼。
首先,需要說明的是不同的SDK版本對AsyncTask的實現不太一樣。
這裡以Android-23 SDK為例。
首先寫下一段程式碼,如上,非常簡單,這段程式碼可以直接丟在onCreate裡面呼叫。意思是說建立一個執行緒,然後執行緒裡面建立一個AsyncTask物件,並且啟動。new Thread(){ public void run(){ System.out.println("Thread is running."); AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() { @Override protected String doInBackground(Void... params) { Log.v("AsyncTask", "doInBackground"); return "hello"; } @Override protected void onPostExecute(String result) { Log.v("AsyncTask", result); } }; task.execute(); } }.start();
跑一下看看:
圖1
11號執行緒裡面建立AsyncTask並且啟動,沒問題。
然後看看doInBackground在哪裡呼叫?
12號執行緒,這是ThreadPoolExecutor建立的執行緒,用來跑task,也沒有問題。
那麼這裡有個問題了,AsyncTask是11號執行緒建立的,那麼onPostExecute是不是會在11號執行緒裡面跑呢?
看事實:
暈了,居然是主執行緒。這是怎麼回事呢?
不是說handler是在AsyncTask內部的嗎?而AsyncTask是11號執行緒建立的啊,那照道理12號執行緒通過handler發過來的訊息應該是在11號執行緒啊。
顯然程式碼不會騙人。我們看進去AsyncTask的實現,看到InternalHandler的實現就明白了。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
注意InternalHandler的建構函式,這傢伙把主執行緒的looper傳給了handler,所以handlerMessage在主執行緒被呼叫了。
也就是說針對Android-23,無論在哪個執行緒建立AsyncTask,最終的回撥還是會在主執行緒。我想這大概是google故意設計的吧。
至於為什麼會有人說只能主執行緒建立AsyncTask物件,我想可能是以前的版本有這個問題吧,可能後來的版本google強制永遠在主執行緒呼叫回調了。這個沒確認過,僅供參考。
那麼從使用者的角度來講:
1. 假如在主執行緒建立AsyncTask,那麼沒有任何問題。
2. 假如在其他執行緒建立AsyncTask,那麼從Android-23開始,肯定沒問題。對於23以前,我沒有去看過,假如之前的版本回調可能在其他執行緒呼叫,那麼如果我們不在回撥裡面處理UI,那也是沒有問題的。