AsyncTask源碼解析

AsyncTask是用來在實現在子線程執行任務後切換到UI線程,常見的比如下載任務的更新,執行下載後在UI線程實時更新進度條。由於是基本的API所以還是很有必要了解源碼的。

使用

使用其實也很簡單,主要是實現AsyncTask類,然後執行excute方法。其中有3個參數和4個方法是必須要掌握的。

  private class MyAsyncTask extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }

        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected String doInBackground(String... strings) {
            return null;
        }
    }

onPreExecute

這個方法是在UI線程執行,並且在後臺任務開始前執行。

doInBackground

這個在線程執行,這裏主要是做耗時任務。這個方法有參數和返回值,參數就是在調用execute傳進來的參數,返回值就是我們經過一些列操作後返回給onProgressUpdate方法的,主要用於UI的更新。例如我下載到50%然後同志UI實時更新。

onProgressUpdate

從方法名看就是更新進度,這個參數就是doInBackground中返回的。

onPostExecute

這個方法就是任務執行完成後要執行的方法。參數也是執行的結果。

三個參數

掌握了這4個方法,這三個參數就很容易理解了。

第一個,是傳入的參數。

第二個,是執行任務時進度更新的值。

第三個,任務執行結束的返回值。

源碼

其實源碼無非就是看內部的核心機制,和這幾個方法的調用流程。最直觀的就從execute方法開始。

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

這裏直接調用了executeOnExecutor,傳入了sDefaultExecutor和參數。

 @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;
    }

先看下這個函數裏面都做了什麼,在分析sDefaultExecutor。executeOnExecutor這個函數是public的所以我們也可以直接通過這個函數執行任務。如果我們通過executeOnExecutor()這個方法的話就要自己創建一個Executor,因爲默認的Executor是使用隊列維護同時只有一個線程執行任務,多任務時會被添加到隊列中,後面會講到。

首先,進來先判斷狀態,如果如果不是沒執行過的要拋出異常,這就是爲什麼如果執行兩次任務會報java.lang.IllegalStateException這個錯的原因。

其次,將狀態改爲正在執行。

然後,執行onPreExecute()方法,這就是爲什麼這個方法在主線程,而且在任務前執行。

再然後開始執行Executor的任務,這時候我們可以分析sDefaultExecutor了。

類裏可以看到

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

這兩個常量其實就是默認創建了一個Executor


    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);
            }
        }
    }

這個SerialExecutor裏面維護了一個隊列,然後通過offer()在隊列的末尾加入任務,通過poll()在頭部檢索出任務並刪除,檢索後賦值給mActive。因爲首次mActive爲null,所以加入了判斷mActive==null的時候執行scheduleNext(),後面再有任務來的話mActive就不是null了,所以是在runable中,通過finally調用了scheduleNext(),這時候就會等任務執行完成後在執行下一個任務。這就是上面說的爲什麼默認的SerialExecutor只能同時在一個線程執行任務。

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

這個主要就是通過各種默認爲的參數創建了一個線程池去執行任務。

這就是主要流程,但是在這個流程中還忽略了很多內容,在我們瞭解了整體框架流程後,在去填充我們想知道的細節,就更容易理解了。

首先看executeOnExecutor()方法中有兩個很重要的內容一個是mWorker和mFuture。其實執行任務的時候傳入了mFuture,而mFuture是一個mFutureTask真正執行的是mWorker。

     /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        this((Looper) null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        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);
                }
            }
        };
    }

在構造裏創建了三部分內容,mHandler、mWorker、mFutur

可以看到首先會創建handler因爲,線程通信還是需要依靠Handler。然後mWorker中主要就是執行doInBackground()然後將result傳給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發送一個what爲MESSAGE_POST_RESULT,obj爲result的message。

 private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @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;
            }
        }
    }
    

這時候看handleMessage是怎麼處理的。
這裏主要是調用區分what是完成還是未完成完成的話就是調用finish方法,未完成就是onProgressUpdate方法,

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

在看finish方法就是判斷是否取消然後調用onPostExecut方法將結果傳入。
hadlerMessage還處理一個就是更新進度,那這個message是在哪裏發的呢?

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

可以看到是通過publishProgress這個方法,所以我們如果需要更新進度的話要自己在doInBackground這個方法裏調用publishProgress()方法傳入進度。然後在纔會在onProgressUpdate()這個方法中收到。

總結

至此 整個流程也就清晰了,這四個方法是分別在哪裏調用的在能實現異步然後更新UI的功能,其實原理很簡單,就是用了一個線城池然後我們在子線程實時通過Handler發送Message到UI線程中執行進度的任務,默認通過一個隊列維護任務,也可以自定義Executor來實現多線程執行任務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章