【Android】AsyncTask源碼分析

在Android中ui是非線程安全的,更新ui只能在主線程操作,所以我們平時如果遇到子線程更新UI的情況,必須要切換到主線程上之后再執行,Handler消息機制就是一種處理方案,為了更方便的處理異步任務以及更新操作,Android在很早的版本就提供了AsyncTask類供我們處理短耗時的異步任務,方便我們子線程處理耗時任務,主線程更新UI。

AsyncTask其實相當于是Handler和Thread的一個封裝,下面我們來看下它的用法吧!

1.AsyncTask的基本使用

我們來看一個簡單的例子:

class MyAsyncTask extends AsyncTask<String,Integer,Boolean>{

        @Override
        protected void onPreExecute() {
            //在doInBackground之前執行
            Log.e("wangkeke","----onPreExecute      thread: "+ Thread.currentThread().getName());
        }

        @Override
        protected Boolean doInBackground(String... strings) {
            Log.e("wangkeke","----doInBackground  thread: "+ Thread.currentThread().getName());

            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(500);
                    //更新進度
                    publishProgress(i*10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            return true;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            Log.e("wangkeke","----onProgressUpdate當前下載進度:"+values[0]+"   thread: "+ Thread.currentThread().getName());
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            Log.e("wangkeke","----onPostExecute任務結束  result = "+aBoolean.booleanValue()+"      thread: "+ Thread.currentThread().getName());

        }
    }

要使用AsyncTask處理異步任務必須要是繼承了它的子類,我們創建了MyAsyncTask類,我們必須重寫它的doInBackground方法,當然其他幾個方法也很重要一般也要重寫,幾個關鍵方法的具體含義如下:
a. onPreExecute:任務開始時會先調用它,它在doInBackground之前執行,用于做一些初始化的工作,它運行在主線程。
b. doInBackground:就像它的名字那樣,它用來在子線程中處理耗時操作。
c. onProgressUpdate:用來接收publishProgress()方法傳遞過來的進度,運行在主線程中。
d. onPostExecute:doInBackground方法執行結束后會調用它,它的參數就是doInBackground的返回值

下面我們來運行一下這個任務:

new MyAsyncTask().execute("start");

嗯,代碼很簡單,執行AsyncTask的execute方法就可以了,看下運行結果:


運行結果

我們可以發現AsyncTask類有三個泛型參數,下面介紹一下三個參數的具體含義,AsyncTask<Params, Progress, Result>,Params參數就是execute方法里傳遞過來的,它的類型是不定長數組,Progress參數代表onProgressUpdate方法的參數類型,Result參數代表onPostExecute方法的參數類型,如果不需要返回類型指定Void即可。

雖然AsyncTask可以用來執行耗時操作,但是官方建議我們最好用于短操作,幾秒鐘之內的操作,如果是長時間運行的任務,建議使用Executor, ThreadPoolExecutor,FutureTask等。

取消AsyncTask任務:我們可以調用cancel(boolean)方法來取消任務,調用了cancel方法之后,我們可以通過判斷isCancel()的返回值是否為true來判斷任務取消是否成功,為了盡快取消任務,可以用它來打破for循環。

幾個注意點:

1.AsyncTask的類必須在UI線程中加載(android 4.1之后會自動處理),創建和調用。

2.不要主動調用onPreExecute,onPostExecute, doInBackground, onProgressUpdate方法。

3.一個AsyncTask對象只能執行一次execute方法,否則會拋出IllegalStateException異常。

4.AsyncTask在1.6之前是串行執行的,1.6之后允許多個任務并行執行,但3.0之后再次改為串行執行任務(為避免并行執行引發的錯誤)。

2.AsyncTask串行和并行

測試一下AsyncTask的串行和并行,看看是不是像我們上面說的那樣,寫個簡單的例子,每個任務睡眠2秒后打印當前時間:

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

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected String doInBackground(String... strings) {

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return strings[0];
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
        }

        @Override
        protected void onPostExecute(String taskName) {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.e("wangkeke",taskName+ "任務結束  時間 = "+df.format(new Date()));
        }
    }

開啟3個任務測試:

new MyAsyncTask().execute("one");
new MyAsyncTask().execute("two");
new MyAsyncTask().execute("three");

我是在Android 4.3系統運行的,運行結果如下,可以看到多任務是串行執行:


Android 4.3系統運行結果:串行執行

本來想啟動個1.6-3.0的模擬器試試的,但是很遺憾,沒有找到版本這么低的模擬器,因為現在Android版本普遍已經是4.0+了,這里就不測試了,感興趣的同學可以自己嘗試下。

3.AsyncTask源碼分析

話不多說,從new MyAsyncTask().execute("start")開始:

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

把傳入的params和sDefaultExecutor作為參數,調用了executeOnExecutor方法,sDefaultExecutor是什么呢:

//sDefaultExecutor的定義如下
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

//SERIAL_EXECUTOR的定義如下,注意SerialExecutor對象,下面要用到
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

sDefaultExecutor其實就是android中的線程池的概念,接著看executeOnExecutor方法:

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //開啟的AsyncTask任務必須是處于等待執行狀態的,如果已經在運行或者已經執行完成,則拋出異常信息
        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)");
            }
        }
        //開始執行把狀態置為RUNNING
        mStatus = Status.RUNNING;
        //之前也說了,onPreExecute會先執行用于一些初始化的操作
        onPreExecute();
        //把參數保存在了mWorker中,mWorker是一個Callable對象
        mWorker.mParams = params;
        //這里調用了Executor的execute方法
        exec.execute(mFuture);
        return this;
    }

這段代碼也很簡單,最后調用了Executor的execute方法,并傳遞了一個FutureTask,這個exec就是上面的sDefaultExecutor對象,而上面說了它其實就是SerialExecutor對象,看下SerialExecutor的execute方法:

    private static class SerialExecutor implements Executor {
        //隊列存儲每次的異步任務
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //當前正在執行的AsyncTask任務
        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);
            }
        }
    }

這里的r就是之前傳遞過來的FutureTask,看下它的定義:

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

    mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //mTaskInvoked標識任務是否被執行過,這里置為true
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //開始調用doInBackground執行異步任務
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

上面分析到SerialExecutor的execute方法,內部會調用r.run(),這里的r是futuretask,于是找到futuretask的run方法,發現會執行FutureTask<Result>(mWorker)構造函數中mWorker的call方法,mWorker的定義代碼在上面列出來了,它調用了doInBackground方法,當futuretask的run方法執行完畢后,會回調自身的done方法。

從上面的代碼可以看出done方法中調用了postResultIfNotInvoked,看下它的代碼:

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        //此時wasTaskInvoked為true,WorkerRunnable中call方法置為true的
        if (!wasTaskInvoked) {
            //發送執行結果
            postResult(result);
        }
    }

    private Result postResult(Result result) {
        //發送Handler消息
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

Handler定義如下:

    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

    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:
                    // 調用自身AsyncTask的finish方法
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //接收到MESSAGE_POST_PROGRESS消息,更新進度,回調onProgressUpdate方法
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

    private void finish(Result result) {
        //如果已經撤銷調用onCancelled,否則把結果傳遞給onPostExecute。
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

到這里已經執行完畢了,之前doInBackground沒有介紹,現在來看下:

@WorkerThread
protected abstract Result doInBackground(Params... params);

它是個抽象方法,需要我們自己去實現,記得我們更新進度的時候,調用了publishProgress方法,來看下它的代碼:

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

超級簡單,直接用Handler發送了一個MESSAGE_POST_PROGRESS的消息,并且把當前AsyncTask的實例和進度值values傳遞給handleMessage,handler接收到MESSAGE_POST_PROGRESS消息后開始回調onProgressUpdate:

case MESSAGE_POST_PROGRESS:
      result.mTask.onProgressUpdate(result.mData);
      break;

onProgressUpdate其實是個空方法,我們自己根據業務邏輯實現即可:

    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

至此,AsyncTask調用邏輯分析完成。

等等,好像有個方法我們還沒說,那就是scheduleNext方法:

    protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }

如果當前沒有任務在執行,就調用scheduleNext執行下一個任務,首先通過判斷mTasks.poll()方法刪除隊列中的第一個元素,并把其返回值(它的返回值其實就是當前要執行的task,先移除隊列,再執行的意思)賦值給mActive,如果獲取到的mActive != null,說明隊列中有任務存在,就交由線程池ThreadPoolExecutor去執行。THREAD_POOL_EXECUTOR的定義如下:

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

4.AsyncTask如何并行執行?

從上面的分析我們知道,AsyncTask在3.0之后默認是串行的,那么如果我們需要并行執行,該怎么做呢?

其實AsyncTask提供了相關的api,上面我們執行AsyncTask是通過execute方法,它還有另外一個方法executeOnExecutor,就是用來并行執行任務的,下面我們來看下它的基本用法:

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

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected String doInBackground(String... strings) {

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return strings[0];
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
        }

        @Override
        protected void onPostExecute(String taskName) {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.e("wangkeke",taskName+ "任務結束  時間 = "+df.format(new Date()));
        }
    }

測試的代碼還是上面的代碼,我們通過executeOnExecutor開啟任務:

new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"one");
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"two");
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"three");

運行結果如下:


并行執行

可以看到,確實并行執行了任務。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容