AsyncTask 程式碼分析

samay發表於2018-12-18

在Android實際開發過程設計到網路圖片下載等耗時操作,需要使用執行緒進行處理。子執行緒和UI主執行緒的互動處理主要通過Thread + Handler方式進行處理。google為了方便開發人員開發,在SDK中引入AsyncTask,使用它可以
簡單靈活地從子執行緒切換UI主執行緒。本文主要從AsyncTask原始碼角度分析該類。

AsyncTask使用

首先AsyncTask是一個抽象類,因此如果我們需要使用它,我們必須建立一個繼承它的子類.

其次我們實現該子類的時候需要指定3個泛型引數,這三個引數的用途如下:

1.Parmas:在執行AsyncTask時需要傳入的引數,可用於在後臺任務中使用。

2.Progress:後臺執行任務時,如果需要在UI執行緒中顯示進度,使用該泛型作為進度單位

3.Result:當任務執行完畢後,如果需要返回結果,使用該泛型作為返回值型別

原始碼描述如下圖:

AsyncTask簡單使用方式如下:

    class SampleTask extends AsyncTask<Void, Integer, Boolean> {  
         .....  
         @Override  
         protected Boolean doInBackground(Void... params) { 
            .....
         }
         @Override  
         protected void onProgressUpdate(Integer... values) {  
            ......
         }
         @Override  
         protected void onPostExecute(Boolean result) {  
            .......    
         }
         ......
    }複製程式碼

如上述程式碼,定義第一個泛型為Void,表示在執行後臺任務時不需要傳入引數;定義第二個泛型是Integer,表示使用整數型別作為進度顯示型別;定義第三個
泛型為Boolean,表示使用Boolean型別作為後臺任務完成返回型別。

如上述程式碼所示,我們定義的AsyncTask重寫了3個辦法,這3個方法和之前定義的三個泛型有直接聯絡。除了這3個,還有常用的方法為onPreExecute。現在我們介紹一下這個4個方法。

1.onPreExecute:該方法會在後臺任務執行之前呼叫,其執行在UI執行緒中。主要作用是在介面上有一些初始化操作,比如顯示一個進度條。

2.doInBackground:該方法是使用AsyncTask必須重寫的方法,其執行在子執行緒中。主要作用是執行一些耗時操作。

3.onProgressUpdate:該方法是在後臺任務中呼叫punlishProgress方法之後執行,它傳入的引數泛型是Progress,上述程式碼就指代Integer。主要作用是在後臺任務執行過程中和UI執行緒實現通訊。

4.onPostExecute:該方法是後臺任務執行完畢並通過return返回時,呼叫該方法。該方法主要作用是在UI執行緒中獲知任務執行完畢或得到返回結果。

上述程式碼完整實現可以如下:

    class SampleTask extends AsyncTask<Void, Integer, Boolean> {  
         @Override  
             protected void onPreExecute() {  
                 progressDialog.show();  
             }  

             @Override  
             protected Boolean doInBackground(Void... params) {  
                 try {  
                     while (true) {  
                         int downloadPercent = doDownload();  
                         publishProgress(downloadPercent);  
                         if (downloadPercent >= 100) {  
                             break;  
                         }  
                     }  
                 } catch (Exception e) {  
                     return false;  
                 }  
                 return true;  
             }  

             @Override  
             protected void onProgressUpdate(Integer... values) {  
                 progressDialog.setMessage("當前下載進度:" + values[0] + "%");  
             }  

             @Override  
             protected void onPostExecute(Boolean result) {  
                 progressDialog.dismiss();  
                 if (result) {  
                     Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();  
                 } else {  
                     Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();  
                 }  
             }  
    }複製程式碼

啟動該AsyncTask如下述程式碼

    new SampleTask().execute();複製程式碼

AsyncTask原始碼分析

如上述程式碼,我們發現該AsyncTask執行之前需要先new出一個物件,那我們先看AsyncTask的構建函式是怎麼樣?

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //定義執行緒優先順序
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
          Params[] mParams;
    }複製程式碼

由AsyncTask的建構函式我們可以看到,在建構函式主要定義2個變數mWorker,mFuture。其中mWorker是Callable物件,mFuture是FutureTask物件,mWorker會作為引數傳入到mFuture中。

構建完成之後將會執行execute方法

    @MainThread
        public final AsyncTask<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }複製程式碼

execute方法將呼叫executeOnExecutor方法,並傳入sDefaultExecutor以及之前定義泛型Params引數。sDefaultExecutor的定義如下

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }複製程式碼

如程式碼所示sDefaultExecutor其實就是SerialExecutor.

SerialExecutor以static修飾,那麼無論new 多少個AsyncTask都只有一個SerialExecutor物件。SerialExecutor是使用ArrayDeque這個佇列來管理Runnable。如果我們一次性啟動了很多個任務,首先在第一次執行execute方法時,會呼叫ArratDeque的offer方法將
傳入Runnable物件新增到Deque的佇列的尾部,然後判斷mActive物件是不是等於null。如果是第一次執行當然是null,那麼他們會呼叫scheduleNext方法。在這個佇列中頭部取值,並賦值給mActive,然後呼叫THREAD_POOL_EXECUTOR去執行取出來的Runnable.如果後續
還有執行緒加入在執行offer新增到佇列中。
那之後新增到ArrayDeque是怎麼執行?我們可以看到offer方法傳入的Runnable匿名類,該類中使用try finally方法。當之前的執行緒執行完成之後,在finally中呼叫了scheduleNext,執行後續的程式。該方法也就是說AsyncTask在同一個時刻只能執行一個執行緒,其他執行緒
是出於等待狀態。
也就是表示:原生的AsyncTask是序列執行執行緒的。

        @MainThread
        public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                Params... params) {
            if (mStatus != Status.PENDING) {
                switch (mStatus) {
                    case RUNNING:
                        throw new IllegalStateException("Cannot execute task:"
                                + " the task is already running.");
                    case FINISHED:
                        throw new IllegalStateException("Cannot execute task:"
                                + " the task has already been executed "
                                + "(a task can be executed only once)");
                }
            }
            mStatus = Status.RUNNING;
            onPreExecute();
            mWorker.mParams = params;
            exec.execute(mFuture);
            return this;
        }複製程式碼

在executeOnExecutor我們看到了onPreExecute方法,它在執行後臺任務之前呼叫,證明了上文的說法。同時我們也看到一點:AsyncTask的方法在執行之後不能重複執行。

在executeOnExecutor中執行execute方法實際呼叫到SerialExecutor中execute方法,我們看到這個方法傳入的是Runnable,那這個Runnable是哪個呢?不錯是mFuture這個引數,實際上呼叫的是FutureTask中的run方法

        public void run() {
            if (state != NEW ||
                !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                             null, Thread.currentThread()))
                return;
            try {
                Callable<V> c = callable;
                if (c != null && state == NEW) {
                    V result;
                    boolean ran;
                    try {
                        result = c.call();
                        ran = true;
                    } catch (Throwable ex) {
                        result = null;
                        ran = false;
                        setException(ex);
                    }
                    if (ran)
                        set(result);
                }
            } finally {
                // runner must be non-null until state is settled to
                // prevent concurrent calls to run()
                runner = null;
                // state must be re-read after nulling runner to prevent
                // leaked interrupts
                int s = state;
                if (s >= INTERRUPTING)
                    handlePossibleCancellationInterrupt(s);
            }
        }複製程式碼

我們看到run方法呼叫了callable的call()方法,callable物件事實上是之前傳入的mWorker。也就是呼叫mWorker中的call中方法,該方法中會執行doInBackground這個方法,也就是我們必須重寫後臺任務方法。在call方法中最後會呼叫postResult方法

    private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
    }複製程式碼

postResult採用handler機制將result發到UI執行緒中,


    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_PROGRESS:
                       result.mTask.onProgressUpdate(result.mData);
                       break;
                   case MESSAGE_POST_RESULT:
                       // There is only one result
                       result.mTask.finish(result.mData[0]);
                       break;
               }
           }
       }複製程式碼

之後執行finish方法

        private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }複製程式碼

在finish我們看到了onPostExecute方法。

那onProgressUpdate是怎麼呼叫的呢?在InternalHandler方法中我們其實看到接收到的Message也有MESSAGE_POST_PROGRESS。而這個訊息的傳送時在這個方法中實現的

        protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values)).sendToTarget();
            }
        }複製程式碼

沒錯。就是publishProgress這個方法實現的。這個說明了上文所說的通過呼叫punlishProgress方法和UI執行緒中onProgressUpdate實時通訊。

擴充:如何將AsyncTask的任務執行修改為並行執行?

1.如上文所說AsyncTask預設的執行狀態是序列,那我們希望改成並行應該如何處理的呢?

我們知道AsyncTask之所以是序列執行的是因為SerialExecutor導致,那我們如果希望是並行執行,我們只需要將Executor修改成可以並行執行就可以了。

    Executor exec = new ThreadPoolExecutor(15, 200, 10,  
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());  
    new SampleTask().executeOnExecutor(exec);複製程式碼

2.AsyncTask的初始化是否可以在子執行緒中執行?

可以的,但是android5.1之前版本不可以。原因是:我們知道AsyncTask和主執行緒互動式通過handler方式實現的。但是5.1之前和 5.1之後的版本的handler定義如下,我們5.1之前的版本的使用的預設執行緒的Looper,
5.1之後獲取的是UI執行緒的Looper。在5.1之前的版本如果你的AsyncTask是在子執行緒建立的,那麼很不幸,你的onPreExecute和onPostExecute並非在UI執行緒執行,而是被Handler post到建立它的那個執行緒執行;
如果你在這兩個執行緒更新了UI,那麼直接導致崩潰。

    //android 5.0
   private static class InternalHandler extends Handler {
     @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;
         }
     }

     //android 5.1
     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_PROGRESS:
                            result.mTask.onProgressUpdate(result.mData);
                            break;
                        case MESSAGE_POST_RESULT:
                            // There is only one result
                            result.mTask.finish(result.mData[0]);
                            break;
                    }
                }
            }複製程式碼

參考資料

[1] blog.csdn.net/guolin_blog…

相關文章