1. 程式人生 > >AsyncTask 坑(一) 哪些執行緒可以呼叫AsyncTask

AsyncTask 坑(一) 哪些執行緒可以呼叫AsyncTask

看到很多文章說,只有主執行緒(UI執行緒)可以建立AsyncTask。個人覺得這個說法不對。從AsyncTask的角度講,任何執行緒都可以建立並且使用AsyncTask。之所以會有主執行緒才能建立AsyncTask的說法,我想無非是onPostExecute, onProgressUpdate等回撥裡面想操作UI吧。假如一個後臺執行緒建立了一個AsyncTask物件,並且使用它,那麼當任務完成後,通過handler會發送訊息過來,而handler是在AsyncTask內部的,那麼就擔心handler會在後臺執行緒裡面跑,然後一處理UI就掛了。

實際上是這樣嗎?

事實勝於雄辯,看程式碼。

首先,需要說明的是不同的SDK版本對AsyncTask的實現不太一樣。

這裡以Android-23 SDK為例。

    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();
首先寫下一段程式碼,如上,非常簡單,這段程式碼可以直接丟在onCreate裡面呼叫。意思是說建立一個執行緒,然後執行緒裡面建立一個AsyncTask物件,並且啟動。

跑一下看看:

圖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,那也是沒有問題的。