1. 程式人生 > >AsyncTask我來給你扯會蛋

AsyncTask我來給你扯會蛋

    • (1)要想使用AsyncTask必須繼承實現子類。子類必須至少要重寫一個方法doInBackground,並且絕大多數的情況還要重寫第二個方法onPostExecute,下面是官方的一個簡單的小例子。
      • private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {

             protected Long doInBackground(URL... urls) {

                 int count = urls.length;

                 long totalSize = 0;

                 for (int i = 0; i < count; i++) {

                     totalSize += Downloader.downloadFile(urls[i]);

                     publishProgress((int) ((i / (float) count) * 100));

                     // Escape early if cancel() is called

                     if (isCancelled()) break;

                 }

                 return totalSize;

             }

             protected void onProgressUpdate(Integer... progress) {

                 setProgressPercent(progress[0]);

             }

             protected void onPostExecute(Long result) {

                 showDialog("Downloaded " + result + " bytes");

             }

         }

      • 這是一個下載的小例子,在doInBackgound這個方法中,大家注意看這句話,if (isCancelled()) break;它是寫在了for迴圈裡面的,平時大家下載的時候可能忘記這一點。加上這句話的話,就算我們正在下載中,也是可以直接跳出迴圈的,停止下載任務,這樣更加保險。其他的方法大家都用的很多,這裡就不再說了。

      • 開啟一下AsyncTask去下載更是非常簡單:

        new DownloadFilesTask().execute(url1, url2, url3);

      • (2)AsyncTask的範型型別。

      • 一個非同步任務使用的三種類型如下:

      • 1:第一個引數是傳送到doInBackground這個方法裡面的。

      • 2:進度型別。這個一般是我們在後臺執行任務時,把進度通過publishProgress這個方法來發不出去,負責更新UI的。

      • 3:最後一個引數是結果型別。當我們的doInBackground執行完畢的時候,負責回撥onPostExecute方法,將引數傳遞到這個方法裡面。

      • 注意:不是我們所有傳遞的型別都會被非同步任務使用,如果想要傳遞未被使用的型別,我們只需要傳遞Void。

      • private class MyTask extends AsyncTask<Void, Void, Void> { ... }


      • 上面這種傳遞Void的寫法,我們平時用的也很多。

      • (3):當一個非同步任務執行時,通過4個步驟來執行。

      • 1:onPreExecute,在任務執行前,呼叫UI執行緒,我們可以在這個方法裡面更新UI介面。這一步通常用於設定任務,例如通過在使用者介面顯示一個進度條。

      • 2:doInBackground,在onPreExecute這個方法呼叫完畢後,會立即在後臺執行緒中呼叫。這個方法一般用來執行比較耗時的後臺計算。非同步任務的引數被傳遞到這個方法裡面,後臺任務執行完畢後必須返回計算的結果,並將結果傳遞到最後一個步驟中。這一步也可以使用publishprogress(Java Object。[ ])釋出一個或多個單位的進展。這些值被公佈在UI執行緒,在onProgressUpdate(Java Object。[ ])的步驟。

      • 3:onProgressUpdate(Java Object。[ ])這個方法會在UI執行緒更新介面顯示在呼叫publishprogress後(Java Object。[ ])。我們一般在doInBackground這個方法中呼叫publishprogress這個方法更新介面,例如我們可以用來更新下載進度條的進度。

      • 4:在doInBackground這個方法執行完畢後會回撥onPostExecute這個方法,並且將doInBackgound計算完畢後的結果以引數的形式傳遞到onPostExecute這個方法中。

      • (4):取消一個Task。

      • 一個非同步執行緒可以在任何時候通過呼叫cancel方法來取消。呼叫這個方法後,當我們呼叫isCancelled() 這個方法的時候會返回true。當我們呼叫cancel方法後,doInBackground這個方法執行完畢後將不會再呼叫onPostExecute這個方法,將會呼叫onCancelled這個方法。為了確保非同步任務儘快的取消,如果可能的話(一個迴圈中),你應該始終檢查isCancelled()這個方法的返回值。

      • (5)執行緒規則

      • 為了讓AsyncTask正常的工作,你應該遵守下面這幾個規則。

      • 1:AsyncTask必須在UI執行緒中開啟(?why)。在android4.1.x及以上已經自動在UI執行緒中載入。(此處可能理解有誤。)

      • 2:Task的物件必須在UI執行緒中建立。(?why)

      • 3:我們啟動執行緒的execute這個方法,必須在UI執行緒中呼叫。

      • 4:不要手動呼叫onPreExecute(), onPostExecute(java.lang.Object), doInBackground(java.lang.Object[]), onProgressUpdate(java.lang.Object[]) 這幾個方法。

      • 5:非同步任務只能被執行一回,如果我們試圖執行第二回的時候,會丟擲一個異常。

      • (6)記憶體方面

      • 1:AsyncTask保證所有的回撥是同步的,除非你顯示同步(這裡理解為你自己去做同步);

      • 2:沒法翻譯了。。。說白了,就是你按照正常的步驟走,不會出問題。

      • (7)執行順序

      • 1:當第一次介紹的時候,AsyncTask是一個後臺執行的序列執行緒。但是從Build.VERSION_CODES.DONUT,android1.6開始,變成了一個後臺並行執行的執行緒池。從Build.VERSION_CODES.HONEYCOMB,android3.0開始,又變成了任務在一個單一的執行緒中來執行,這是為了避免常見的併發錯誤。如果你真的想要的並行執行,你可以呼叫executeonexecutor與thread_pool_executor。

      • 上面介紹完了AsyncTask要我們注意的事項,接下來我們就來分析AsyncTask的原始碼,看看裡面到底是一個什麼東東。AsyncTask的原始碼並不多。我們先看它的成員變數。

      • private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        181    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
        182    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        183    private static final int KEEP_ALIVE = 1;
        184
        185    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        186        private final AtomicInteger mCount = new AtomicInteger(1);
        187
        188        public Thread More ...newThread(Runnable r) {
        189            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        190        }
        191    };
        192
        193    private static final BlockingQueue<Runnable> sPoolWorkQueue =
        194            new LinkedBlockingQueue<Runnable>(128);
        public static final Executor THREAD_POOL_EXECUTOR
        200            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
        201                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 
        public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
        208
        209    private static final int MESSAGE_POST_RESULT = 0x1;
        210    private static final int MESSAGE_POST_PROGRESS = 0x2;
        211
        212    private static final InternalHandler sHandler = new InternalHandler();
        213
        214    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
        215    private final WorkerRunnable<Params, Result> mWorker;
        216    private final FutureTask<Result> mFuture;
        217
        218    private volatile Status mStatus = Status.PENDING;
        219    
        220    private final AtomicBoolean mCancelled = new AtomicBoolean();
        221    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

        我們一個一個分析變數,看一下到底是個什麼東東~
      • (1)cpu_count 這個就不用多說了,是在執行時獲取手機的cpu的數量,例如我的手機雙核,那麼count就是2.

      • (2)CORE_POOL_SIZE,同一時刻能夠執行的執行緒數的數量。當執行緒數量超過這個數目時,其他執行緒就要等待。

      • (3)MAXIMUM_POOL_SIZE,執行緒池的總大小,當我們試圖新增超過這個數量的執行緒時,程式就會崩潰。

      • (4)KEEP_ALIVE,當前活躍的執行緒的數量,這裡我們看到為1,也就是序列執行,這個後面就會在原始碼看到為什麼時序列執行。

      • (5)sThreadFactory,這是一個執行緒工廠。ThreadFactory是一個介面,我們直接new一個介面就相當於寫了一個繼承這個介面的子類。這樣做的好處是我們就不用手動建立執行緒了,也就是不用自己去new Thread了。下面是一個簡單的小例子。

      • package com.test;  
          
        import java.util.concurrent.ExecutorService;  
        import java.util.concurrent.Executors;  
        import java.util.concurrent.ThreadFactory;  
          
        class Task implements Runnable{  
            int taskId;  
            public Task(int taskId) {  
                this.taskId=taskId;  
            }  
              
            @Override  
            public void run() {  
                System.out.println(Thread.currentThread().getName()+"--taskId: "+taskId);  
                  
            }  
        }  
          
        class DaemonThreadFactory implements ThreadFactory {  
            @Override  
            public Thread newThread(Runnable r) {  
                Thread t=new Thread(r);  
                t.setDaemon(true);  
                return t;  
            }  
              
        }  
        public class ThreadFactoryTest {  
            public static void main(String[] args) {  
                ExecutorService exec=Executors.newFixedThreadPool(3,new DaemonThreadFactory());  
                for(int i=0;i<3;i++) {  
                    exec.submit(new Task(i));  
                }  
                exec.shutdown();  
            }  
        }  

        這一般是執行緒工廠的用法,DaemonThreadFactory中覆寫的newThread()方法與submit()方法的呼叫關係,也就是說DaemonThreadFactory是如何起作用的。submit()時會呼叫DaemonThreadFactory類的newThread()方法來建立執行緒。
      • 這裡相信大家都明白了。

      • (6)sPoolWorkQueue,這個變數是從來儲存Runnable的一個BlockingQueue<Runnable>,關於BlockingQueue,大家可以上網查詢一些資料,這裡大家就先理解為一個執行緒安全的佇列。

      • (7)THREAD_POOL_EXECUTOR,執行緒池,如果要AsyncTask並行執行的話,後面需要用到。

      • (8)MESSAGE_POST_RESULT,一個int型變數,在doInBackground方法執行完畢後,會通過handler將執行結果分發到onPostExecute這個方法裡面,所以我們才可以在這個回撥方法裡面更新UI介面。

      • (9)MESSAGE_POST_PROGRESS,一個int型變數,當我們在doInBackground需要更新進度條顯示的時候,需要通過handler分發訊息,其中訊息的what就是MESSAGE_POST_PROGRESS這個變數。

      • (10)sHandler,繼承系統Handler實現的一個簡單的handler類,用來分發訊息到主執行緒,不然怎麼更新介面。

      • (11)sDefaultExecutor,就是上面我們講解的THREAD_POOL_EXECUTOR這個變數。

      • (12)mWorker,一個WorkerRunnable<Params, Result>變數,下面看一下這個變數的型別的真面目。

      • private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        655        Params[] mParams;
        656    }
        一個自定義的抽象類,繼承了Callable介面,這個介面中只有一個方法,下面看一下Callable介面的原始碼。
      • public interface Callable<V> {
        64    V call() throws Exception;
        65}
        這裡應該想到了,就是簡單實現了一個介面,這樣我們在其他的地方就可以呼叫call方法了。
      • 這個方法中還有一個Result型別,我們看看這是個什麼型別。

        public abstract class AsyncTask<Params, Progress, Result> {

        這是範型,大家可以看到Params,Progress,Result全是定義的範型。
      • (13)mFuture,這是一個FutureTask<Result>型別,我們繼續看這是一個什麼東東~

      • public class FutureTask<V> implements RunnableFuture<V> {
        public interface RunnableFuture<V> extends Runnable, Future<V> {

        public interface More Future<V> {

        就是實現了兩個介面,實現了Future其中的幾個方法,實現了Runnable裡面的run方法,作用就是隻要實現了RunnableFuture這個介面,我們就可以回撥裡面的方法了~很簡單的設計模式,主動呼叫。
      • (14)mStatus,用來記錄AsyncTask的執行狀態,有PENDING,  RUNNING,FINISHED,這三個狀態,Pending狀態說明AsyncTask還沒有被執行,等待狀態。Running正在執行狀態。Finished完成狀態。

      • (15)mCancelled,一個AtomicBoolean型別變數,解決執行緒同步問題。當前AsyncTask是否被取消。

      • (16)mTaskInvoked,一個AtomicBoolean型別變數,解決執行緒同步問題。當前AsyncTAsk是否被啟動。

        上面介紹完了AsyncTask的成員變數,下面我們就從AsyncTAsk的建構函式開始,一步一步分析AsyncTask是如何工作的,最後我們還要看看原始碼,明白一下為什麼我們使用AsyncTask要遵守Android我們定的準則,我們不遵守可以嗎?

      • 看看AsyncTask的建構函式:

      • public AsyncTask() {
        282        mWorker = new WorkerRunnable<Params, Result>() {
        283            public Result call() throws Exception {
        284                mTaskInvoked.set(true);
        285
        286                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        287                //noinspection unchecked
        288                return postResult(doInBackground(mParams));
        289            }
        290        };
        291
        292        mFuture = new FutureTask<Result>(mWorker) {
        293            @Override
        294            protected void done() {
        295                try {
        296                    postResultIfNotInvoked(get());
        297                } catch (InterruptedException e) {
        298                    android.util.Log.w(LOG_TAG, e);
        299                } catch (ExecutionException e) {
        300                    throw new RuntimeException("An error occured while executing doInBackground()",
        301                            e.getCause());
        302                } catch (CancellationException e) {
        303                    postResultIfNotInvoked(null);
        304                }
        305            }
        306        };
        307    }
        從構造方法中我們可以揣測到,mWorker這個物件一定在我們呼叫execute的時候會回撥call方法,因為mTaskInvoked.set(true);這個標誌為被設定成了true,說明AsyncTask被呼叫執行了。緊接著我們在後面的return語句後面發現了postResult(doInBackground(mParams));這句話,我擦,這不是我們doInBackground方法嗎?把doInBackground方法的結果返回去了,反到哪裡去了?不就是反到了我們平時呼叫的onPostExecute方法裡面的引數了嘛。這個方法裡面我們已經初步看到了一點蛛絲馬跡。那麼postResult這個方法裡面是什麼東東?
      • private Result postResult(Result result) {
        317        @SuppressWarnings("unchecked")
        318        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
        319                new AsyncTaskResult<Result>(this, result));
        320        message.sendToTarget();
        321        return result;
        322    }

        soga,不他媽就是簡單的handler傳送訊息嘛!訊息的型別是什麼?當然是這個型別的MESSAGE_POST_RESULT,哈哈
      • 說到這裡我們就繼續看一眼sHandler這個handler裡面的訊息處理,順序來吧,一回反回去,再講解mFuture這個變數~ps:我喜歡順序講解,一步一步看。。。

      • private static class AsyncTaskResult<Data> {
        660        final AsyncTask mTask;
        661        final Data[] mData;
        662
        663        AsyncTaskResult(AsyncTask task, Data... data) {
        664            mTask = task;
        665            mData = data;
        666        }
        667    }
        再看sHandler的原始碼之前,先low一眼AsyncTaskResult這個類的程式碼,發現就是簡單的兩個成員變數~so easy
      • private static class InternalHandler extends Handler {
        638        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        639        @Override
        640        public void handleMessage(Message msg) {
        641            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        642            switch (msg.what) {
        643                case MESSAGE_POST_RESULT:
        644                    // There is only one result
        645                    result.mTask.finish(result.mData[0]);
        646                    break;
        647                case MESSAGE_POST_PROGRESS:
        648                    result.mTask.onProgressUpdate(result.mData);
        649                    break;
        650            }
        651        }
        652    }
        好,在sHandler的重寫的handleMessage方法中,我們可以看到最終的呼叫,MESSAGE_POST_RESULT這個訊息下面,呼叫的是result.mTask.finish(result.mData[0]);而mTask就是當前的AsyncTask,接下來我們就看一下finish方法裡面的呼叫。
      • private void finish(Result result) {
        629        if (isCancelled()) {
        630            onCancelled(result);
        631        } else {
        632            onPostExecute(result);
        633        }
        634        mStatus = Status.FINISHED;
        635    }
        果然不出所料,在finish方法裡面根據isCancelled()方法呼叫onCancelled或者呼叫onPostExecute。豁然開朗啊,同理result.mTask.onProgressUpdate(result.mData);這句程式碼也就沒有什麼大驚小怪的了,這不就是更新進度顯示的回撥方法嘛!草,就是handler傳送訊息啊!
      • 說了這麼多,接下來我們該回到我們構造函數了。。。下面這個初始化變數我們還沒有講解~

      •  mFuture = new FutureTask<Result>(mWorker) {
        293            @Override
        294            protected void done() {
        295                try {
        296                    postResultIfNotInvoked(get());
        297                } catch (InterruptedException e) {
        298                    android.util.Log.w(LOG_TAG, e);
        299                } catch (ExecutionException e) {
        300                    throw new RuntimeException("An error occured while executing doInBackground()",
        301                            e.getCause());
        302                } catch (CancellationException e) {
        303                    postResultIfNotInvoked(null);
        304                }
        305            }
        306        };
        這個我們猜測一下,我們構建了一個mWorker物件,裡面有一個call的回撥方法,然後我們mWorker物件通過FutureTask的建構函式傳遞進度,並且重寫了裡面的done方法。那mFuture裡面一定會在重寫的介面方法裡面呼叫mWorker這個介面的call方法~那我們接下來看一下FutureTask的類的原始碼吧。
      • public class FutureTask<V> implements RunnableFuture<V> {
            
        64     private final Sync sync;
        
        72 
        73     public FutureTask(Callable<V> callable) {
        74         if (callable == null)
        75             throw new NullPointerException();
        76         sync = new Sync(callable);
        77     }
        
        90 
        91     public FutureTask(Runnable runnable, V result) {
        92         sync = new Sync(Executors.callable(runnable, result));
        93     }
        94 
        95     public boolean isCancelled() {
        96         return sync.innerIsCancelled();
        97     }
        98 
        99     public boolean isDone() {
        100        return sync.innerIsDone();
        101    }
        102
        103    public boolean cancel(boolean mayInterruptIfRunning) {
        104        return sync.innerCancel(mayInterruptIfRunning);
        105    }
        
            
        109
        110    public V get() throws InterruptedException, ExecutionException {
        111        return sync.innerGet();
        112    }
        
            
        116
        117    public V get(long timeout, TimeUnit unit)
        118        throws InterruptedException, ExecutionException, TimeoutException {
        119        return sync.innerGet(unit.toNanos(timeout));
        120    }
        
        130
        131    protected void done() { }
        
            
        139
        140    protected void set(V v) {
        141        sync.innerSet(v);
        142    }
        
            
        151
        152    protected void setException(Throwable t) {
        153        sync.innerSetException(t);
        154    }
        155
        156    // The following (duplicated) doc comment can be removed once
        157    //
        158    // 6270645: Javadoc comments should be inherited from most derived
        159    //          superinterface or superclass
        160    // is fixed.
        161    
        
        164
        165    public void run() {
        166        sync.innerRun();
        167    }
        
            
        176
        177    protected boolean runAndReset() {
        178        return sync.innerRunAndReset();
        179    }
        
        188
        189    private final class Sync extends AbstractQueuedSynchronizer {
        190        private static final long serialVersionUID = -7828117401763700385L;
        
                
        State value representing that task is ready to run
        192
        193        private static final int READY     = 0;
                
        State value representing that task is running
        194
        195        private static final int RUNNING   = 1;
                
        State value representing that task ran
        196
        197        private static final int RAN       = 2;
                
        State value representing that task was cancelled
        198
        199        private static final int CANCELLED = 4;
        
                
        The underlying callable
        201
        202        private final Callable<V> callable;
                
        The result to return from get()
        203
        204        private V result;
                
        The exception to throw from get()
        205
        206        private Throwable exception;
        
                
        212
        213        private volatile Thread runner;
        214
        215        Sync(Callable<V> callable) {
        216            this.callable = callable;
        217        }
        218
        219        private boolean ranOrCancelled(int state) {
        220            return (state & (RAN | CANCELLED)) != 0;
        221        }
        
                
        225
        226        protected int tryAcquireShared(int ignore) {
        227            return innerIsDone() ? 1 : -1;
        228        }
        
                
        233
        234        protected boolean tryReleaseShared(int ignore) {
        235            runner = null;
        236            return true;
        237        }
        238
        239        boolean innerIsCancelled() {
        240            return getState() == CANCELLED;
        241        }
        242
        243        boolean innerIsDone() {
        244            return ranOrCancelled(getState()) && runner == null;
        245        }
        246
        247        V innerGet() throws InterruptedException, ExecutionException {
        248            acquireSharedInterruptibly(0);
        249            if (getState() == CANCELLED)
        250                throw new CancellationException();
        251            if (exception != null)
        252                throw new ExecutionException(exception);
        253            return result;
        254        }
        255
        256        V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
        257            if (!tryAcquireSharedNanos(0, nanosTimeout))
        258                throw new TimeoutException();
        259            if (getState() == CANCELLED)
        260                throw new CancellationException();
        261            if (exception != null)
        262                throw new ExecutionException(exception);
        263            return result;
        264        }
        265
        266        void innerSet(V v) {
        267            for (;;) {
        268                int s = getState();
        269                if (s == RAN)
        270                    return;
        271                if (s == CANCELLED) {
        272                    // aggressively release to set runner to null,
        273                    // in case we are racing with a cancel request
        274                    // that will try to interrupt runner
        275                    releaseShared(0);
        276                    return;
        277                }
        278                if (compareAndSetState(s, RAN)) {
        279                    result = v;
        280                    releaseShared(0);
        281                    done();
        282                    return;
        283                }
        284            }
        285        }
        286
        287        void innerSetException(Throwable t) {
        288            for (;;) {
        289                int s = getState();
        290                if (s == RAN)
        291                    return;
        292                if (s == CANCELLED) {
        293                    // aggressively release to set runner to null,
        294                    // in case we are racing with a cancel request
        295                    // that will try to interrupt runner
        296                    releaseShared(0);
        297                    return;
        298                }
        299                if (compareAndSetState(s, RAN)) {
        300                    exception = t;
        301                    releaseShared(0);
        302                    done();
        303                    return;
        304                }
        305            }
        306        }
        307
        308        boolean innerCancel(boolean mayInterruptIfRunning) {
        309            for (;;) {
        310                int s = getState();
        311                if (ranOrCancelled(s))
        312                    return false;
        313                if (compareAndSetState(s, CANCELLED))
        314                    break;
        315            }
        316            if (mayInterruptIfRunning) {
        317                Thread r = runner;
        318                if (r != null)
        319                    r.interrupt();
        320            }
        321            releaseShared(0);
        322            done();
        323            return true;
        324        }
        325
        326        void innerRun() {
        327            if (!compareAndSetState(READY, RUNNING))
        328                return;
        329
        330            runner = Thread.currentThread();
        331            if (getState() == RUNNING) { // recheck after setting thread
        332                V result;
        333                try {
        334                    result = callable.call();
        335                } catch (Throwable ex) {
        336                    setException(ex);
        337                    return;
        338                }
        339                set(result);
        340            } else {
        341                releaseShared(0); // cancel
        342            }
        343        }
        344
        345        boolean innerRunAndReset() {
        346            if (!compareAndSetState(READY, RUNNING))
        347                return false;
        348            try {
        349                runner = Thread.currentThread();
        350                if (getState() == RUNNING)
        351                    callable.call(); // don't set result
        352                runner = null;
        353                return compareAndSetState(RUNNING, READY);
        354            } catch (Throwable ex) {
        355                setException(ex);
        356                return false;
        357            }
        358        }
        359    }
        360}
        
        這個類的程式碼稍微多一點,但是我們只需要找到我們關心的即可。我們首先找到建構函式~
      • 73     public FutureTask(Callable<V> callable) {
        74         if (callable == null)
        75             throw new NullPointerException();
        76         sync = new Sync(callable);
        77     }
        FutureTask的建構函式需要我們傳遞進去一個實現Callable介面的物件,我們前面mWork物件已經實現了這個介面~。然後對callable物件進一步封裝了一下,放到了Sync這個類裡面,原始碼裡面的解釋是這個類用來對FutureTask進行同步的控制,跟進去看一下這個類的建構函式~
      • Sync(Callable<V> callable) {
        216            this.callable = callable;
        217        }

        建構函式很簡單,就是持有了Callable物件的引用,那麼我們就可以呼叫它的call方法了,哈哈。它裡面的其他方法,我們在後面涉及到的時候再講解~接下來我們分析完了AsyncTask的建構函式,就需要跟進它的啟動過程了。我們通過new 一個AsyncTask,然後呼叫它的execute方法,它就啟動了,這個過程是怎麼樣子的呢?
      •  public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        535        return executeOnExecutor(sDefaultExecutor, params);
        536    }
        這是AsyncTask的啟動方法,繼續跟進~
      • public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        572            Params... params) {
        573        if (mStatus != Status.PENDING) {
        574            switch (mStatus) {
        575                case RUNNING:
        576                    throw new IllegalStateException("Cannot execute task:"
        577                            + " the task is already running.");
        578                case FINISHED:
        579                    throw new IllegalStateException("Cannot execute task:"
        580                            + " the task has already been executed "
        581                            + "(a task can be executed only once)");
        582            }
        583        }
        584
        585        mStatus = Status.RUNNING;
        586
        587        onPreExecute();
        588
        589        mWorker.mParams = params;
        590        exec.execute(mFuture);
        591
        592        return this;
        593    }

        看這個方法,需要兩個引數,第一個是一個執行緒池,這個變數我們前面已經創建出來了,它的原始碼如下:
      • private static class SerialExecutor implements Executor {
        224        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        225        Runnable mActive;
        226
        227        public synchronized void execute(final Runnable r) {
        228            mTasks.offer(new Runnable() {
        229                public void run() {
        230                    try {
        231                        r.run();
        232                    } finally {
        233                        scheduleNext();
        234                    }
        235                }
        236            });
        237            if (mActive == null) {
        238                scheduleNext();
        239            }
        240        }
        241
        242        protected synchronized void scheduleNext() {
        243            if ((mActive = mTasks.poll()) != null) {
        244                THREAD_POOL_EXECUTOR.execute(mActive);
        245            }
        246        }
        247    }
        

        這個類繼承了Exector這個介面,繼續看吧~
      • public interface Executor {
        140    void execute(Runnable command);
        141}
        看來我們重寫了裡面的execute方法,好那我們就看一下SerialExecutor這個類重寫的方法。不難看出,當我們傳遞一個Runnable進去之後,它重新封裝成了一個新的Runnable介面,放到了ArrayDeque這個佇列裡面。下面有一個判斷mActive是否為空,第一次肯定為空,那麼就會呼叫scheduleNext方法,這個方法裡面從ArrayDeque裡面取出一個Runnable,然後放到了THREAD_POOL_EXECUTOR這個執行緒池裡面去執行。在Runnable執行完畢後,無論如何都會在finally裡面呼叫scheduleNext方法,這樣假如我們佇列裡面有很多Runnable的話,就會序列執行下去。
      • 我們再重新看一下THREAD_POOL_EXECUTOR這個變數~

        public static final Executor THREAD_POOL_EXECUTOR
        200            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
        201                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

        第一個引數是同一個時刻能夠執行執行緒的數量;第二個引數是執行緒池的總大小;第三個引數是活躍的執行緒的數量;第四個引數是時間單位;第五個引數是一個執行緒安全的BlockingQueue;第六個引數是一個執行緒工廠,自動產生執行緒。

      • 分析完了executeOnExecutor這個方法的引數,我們進一步看裡面的實現~

      • if (mStatus != Status.PENDING) {
        574            switch (mStatus) {
        575                case RUNNING:
        576                    throw new IllegalStateException("Cannot execute task:"
        577                            + " the task is already running.");
        578                case FINISHED:
        579                    throw new IllegalStateException("Cannot execute task:"
        580                            + " the task has already been executed "
        581                            + "(a task can be executed only once)");
        582            }
        583        }
        584
        585        mStatus = Status.RUNNING;

        首先看上面這部分程式碼,大家一看就明白了當我們一個AsyncTask正在執行狀態的時候,再次呼叫execute方法,會丟擲一個錯誤:不行再次執行一個正在執行的AsyncTask。同理當一個AsyncTask已經執行完畢之後,也不能再次呼叫execute。不是上面的兩種情況的話,就更新當前AsyncTask為正在執行的狀態。
      • onPreExecute();
        588
        589        mWorker.mParams = params;
        590        exec.execute(mFuture);

        此時呼叫的onPreExecute方法意義重大,別人可能就會說了,不就是一個onPreExecute方法的呼叫嗎?這不就是我們平時在下載之前先更新一下介面的onPreExecute方法嘛!對了,就是更新介面的onPreExecute方法。大家現在還沒有忘記AsyncTask的使用準則吧?我們啟動執行緒的execute這個方法,必須在UI執行緒中呼叫。就是這條準則,為什麼AsyncTask必須在UI執行緒中execute,而不能在子執行緒中?這就是原因,因為這個方法android並沒有通過handler傳送出來,而我們平時都在這個方法中更新介面,如果直接在子執行緒中execute,不崩潰才怪!
      • 下面就是將傳遞進來的引數傳遞給mWorker物件的成員變數,而mWorker是封裝在mFuture物件裡面的,所以最後傳遞的是mFuture物件作為引數。exec執行的程式碼參考如下:再次貼一遍程式碼

      • public synchronized void execute(final Runnable r) {
        228            mTasks.offer(new Runnable() {
        229                public void run() {
        230                    try {
        231                        r.run();
        232                    } finally {
        233                        scheduleNext();
        234                    }
        235                }
        236            });
        237            if (mActive == null) {
        238                scheduleNext();
        239            }
        240        }
         
        當然我們的mFuture是實現了Runnable介面的,所以肯定會重寫run方法的,大家可以大膽的猜測一下,mFuture的run方法裡面一定會呼叫mWorker的call方法,不然它怎麼辦~廢話少說,看一下mFuture類中重寫的的run方法~
      • public void run() {
        166        sync.innerRun();
        167    }
        

        繼續跟進~
      • void innerRun() {
        327            if (!compareAndSetState(READY, RUNNING))
        328                return;
        329
        330            runner = Thread.currentThread();
        331            if (getState() == RUNNING) { // recheck after setting thread
        332                V result;
        333                try {
        334                    result = callable.call();
        335                } catch (Throwable ex) {
        336                    setException(ex);
        337                    return;
        338                }
        339                set(result);
        340            } else {
        341                releaseShared(0); // cancel
        342            }
        343        }
        大家在334行看到沒有,果然呼叫了mWorker的call方法,哈哈,說明我們的猜測完全正確~而call方法裡面就是doInBackground的實現了,以後的邏輯就完全聯絡起來了。AsyncTask的整個過程就分析完了。
      • 分析是分析完了,但是還有幾個小小的疑問。

      • 1:AsyncTask為什麼必須在UI執行緒中execute?

      • 因為在execute方法中會呼叫onPreExecute方法去更新介面,所以如果不是在UI執行緒中execute,那麼程式就會崩潰。

      • 2:AsyncTask為什麼必須在UI執行緒中建立?

      • 首先UI執行緒中建立而牽扯出的在AsyncTask中的成員變數,看了一下只有sHandler,而這個Handler的建立肯定會呼叫父類的預設的建構函式,那麼我們就去看一眼handler預設的建構函式,看看裡面是個什麼東東~

      • public More ...Handler(Callback callback, boolean async) {
        189        if (FIND_POTENTIAL_LEAKS) {
        190            final Class<? extends Handler> klass = getClass();
        191            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
        192                    (klass.getModifiers() & Modifier.STATIC) == 0) {
        193                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
        194                    klass.getCanonicalName());
        195            }
        196        }
        197
        198        mLooper = Looper.myLooper();
        199        if (mLooper == null) {
        200            throw new RuntimeException(
        201                "Can't create handler inside thread that has not called Looper.prepare()");
        202        }
        203        mQueue = mLooper.mQueue;
        204        mCallback = callback;
        205        mAsynchronous = async;
        206    }
        

        預設的建構函式最後會呼叫Handler的這個建構函式,這裡面需要跟進一下mLooper,看這個變數是怎麼被初始化的。mLooper = Looper.myLooper();我們就繼續跟進~Handler和Looper的關係很重要,而Looper在初始化的時候就會建立一個MessageQueue佇列。我們看一下Looper的myLooper方法。
      • public static Looper myLooper() {
        162        return sThreadLocal.get();
        163    }
        發現有一個sThreadLocal,就是我們當前建立Handler的執行緒。然後我們繼續跟進去~
      • public T get() {
        143        Thread t = Thread.currentThread();
        144        ThreadLocalMap map = getMap(t);
        145        if (map != null) {
        146            ThreadLocalMap.Entry e = map.getEntry(this);
        147            if (e != null)
        148                return (T)e.value;
        149        }
        150        return setInitialValue();
        151    }
        此時的範型T就是我們的Looper物件,我們就不再繼續往裡面跟了。發現最裡面其實就是一個Map,我們可以根據我們的Thread來得到我們的Looper,對於沒有Looper的Thread肯定是得不到Looper的。得不到Looper是不可能成功的建立Handler的,一建立就會報錯~
      • 具體的關於Handler的請看我的另一篇部落格:宇哥帶你飛之handler~

      • 好了,現在我們就有一個疑問了,我就要在子執行緒中使用AsyncTask,但是我給當前這個執行緒準備好Looper,這樣Handler的建立肯定是沒有問題的。難道是Android官方的表達不夠嚴謹嗎?下面我就寫一個小例子來實驗一下。系統4.0以上-編譯sdk20

      • 第一種寫法:

      • private void init() {
                content = (TextView) findViewById(R.id.content);
                new Thread() {
                    @Override
                    public void run() {
                        Looper.prepare();
                        new AsyncTask<Void, Void, Void>() {
                            @Override
                            protected void onPreExecute() {
                                Log.d("xiaoyu","onPreExecute--"+Thread.currentThread().getId());
                                super.onPreExecute();
                            }
        
                            @Override
                            protected Void doInBackground(Void... params) {
                                for (int i = 0; i < 10; i++) {
                                    try {
                                        Thread.sleep(10);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                return null;
                            }
        
                            @Override
                            protected void onPostExecute(Void aVoid) {
                                super.onPostExecute(aVoid);
                                Log.d("xiaoyu","onPostExecute--"+Thread.currentThread().getId());
                                Toast.makeText(MainActivity.this, "onPostExecute", Toast.LENGTH_LONG).show();
                            }
                        }.execute();
                        Looper.loop();
                    }
                }.start();
            }

        我們先拋開onPreExecute方法不看,根據handler內部建立的機制,handler建立時會自動繫結到當前執行緒,如果當前執行緒沒有Looper會報錯,直接程式崩潰。程式執行沒有崩潰~我現在認為handler得到的是當前執行緒的Looper(現在的理解有錯誤)。
      • 繼續實驗:去掉Loop.prepare(),Loop.loop().再次執行程式,應該報錯才對,為什麼沒有報錯,難道我第一次寫法,AsyncTask得到的根本就不是當前執行緒的looper?找問題,最後在ActivityThread中找到答案~

      • public static void main(String[] args) {
        5009        SamplingProfilerIntegration.start();
        5010
        5011        // CloseGuard defaults to true and can be quite spammy.  We
        5012        // disable it here, but selectively enable it later (via
        5013        // StrictMode) on debug builds, but using DropBox, not logs.
        5014        CloseGuard.setEnabled(false);
        5015
        5016        Environment.initForCurrentUser();
        5017
        5018        // Set the reporter for event logging in libcore
        5019        EventLogger.setReporter(new EventLoggingReporter());
        5020
        5021        Process.setArgV0("<pre-initialized>");
        5022
        5023        Looper.prepareMainLooper();
        5024
        5025        ActivityThread thread = new ActivityThread();
        5026        thread.attach(false);
        5027
        5028        if (sMainThreadHandler == null) {
        5029            sMainThreadHandler = thread.getHandler();
        5030        }
        5031
        5032        AsyncTask.init();
        5033
        5034        if (false) {
        5035            Looper.myLooper().setMessageLogging(new
        5036                    LogPrinter(Log.DEBUG, "ActivityThread"));
        5037        }
        5038
        5039        Looper.loop();
        5040
        5041        throw new RuntimeException("Main thread loop unexpectedly exited");
        5042    }

        大家看5032行的程式碼,在程式啟動的時候,呼叫了AsyncTask 的init方法,我們進去看一眼init方法裡面的實現~
      • public static void init() {
                sHandler.getLooper();
            }
        在主執行緒中建立的AsyTask得到的是主執行緒的Looper,這也就是為什麼我們直接在沒有Looper的子執行緒中建立AsyncTask的物件不會崩潰的原因。但是4.0一下的程式碼不保證AsyncTask一定是得到的是主執行緒的Looper,所以我們還是儘量不要在子執行緒中建立AsyncTask的物件。
      • 2:接下來的問題是我們為什麼必須在UI執行緒中呼叫execute方法呢?在子執行緒呼叫不行嗎?從前面的分析中我們得到因為execute方法呼叫的時候會呼叫到onPreExecute方法,而這個方法我們一般都在裡面更新介面,所以應該在子執行緒中呼叫execute方法,會崩潰才對~

      • new Thread() {
                    @Override
                    public void run() {
                        Log.d("xiaoyu","run--"+Thread.currentThread().getId());
                        new AsyncTask<Void, Void, Void>() {
                            @Override
                            protected void onPreExecute() {
                                Log.d("xiaoyu","onPreExecute--"+Thread.currentThread().getId());
                                super.onPreExecute();
                                content.setText("onPreExecute");
                            }
        
                            @Override
                            protected Void doInBackground(Void... params) {
                                for (int i = 0; i < 10; i++) {
                                    try {
                                        Thread.sleep(10);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                return null;
                            }
        
                            @Override
                            protected void onPostExecute(Void aVoid) {
                                super.onPostExecute(aVoid);
                                Log.d("xiaoyu","onPostExecute--"+Thread.currentThread().getId());
                                Toast.makeText(MainActivity.this, "onPostExecute", Toast.LENGTH_LONG).show();
                            }
                        }.execute();
                    }
                }.start();

        上面這種寫法應該崩潰才對啊?為什麼沒有崩潰啊?並且後臺列印執行緒id,onPreExecute方法的呼叫確實不在主執行緒中~
      • 08-05 10:48:17.810    2406-2406/? D/xiaoyu﹕ mainThreadId--1
        08-05 10:48:17.810    2406-2424/? D/xiaoyu﹕ run--178
        08-05 10:48:17.810    2406-2424/? D/xiaoyu﹕ onPreExecute--178
        08-05 10:48:17.926    2406-2406/? D/xiaoyu﹕ onPostExecute--1

        很奇怪,在子執行緒中竟然更新了介面!!!我了個擦!繼續找問題,難道又是系統給我們做了工作了?但是從列印來看,確實更新介面的時候不是在主執行緒中啊!這個問題還在找中。。。難道是Android給AsyncTask開綠燈了?