Android多線程系列(一) AsyncTask基本使用以及源碼解析

前言

Android中,線程是操作系統調度的最小單位。線程分爲主線程和子線程。主線程用來處理界面的交互,而耗時操作(網絡請求,複雜的數據庫查詢)必須在子線程中來完成。通過Handler消息機制完成主線程和子線程之間的通信。

每個任務都需要一個線程去執行,但是不可能每個任務的執行都是伴隨着線程的銷燬和重新創建,十分的耗費性能。所以用線程池去緩存一定數目的線程,由線程池來管理執行任務的線程,避免了頻繁的創建和銷燬。Android中提供了AsyncTask類,內部就是由線程池Handler實現的。各位大佬,這篇看了包會!

這裏寫圖片描述

(一) AsyncTask使用

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一個抽象類,如果使用的話需要自定義一個類繼承它。它提供了4個核心的方法。這裏用一個示例來做介紹(Kotlin環境下)。

一.使用和核心方法介紹

1.1 自定義類繼承AsyncTask

/*靜態屬性聲明處,此處MyAsyncTask爲靜態類*/
 companion object {

         class MyAsyncTask(private var taskName: String) : AsyncTask<String, Int, String>() {
            /*核心方法1*/
             override fun onPreExecute() {
                super.onPreExecute()
                 Log.d("MyAsyncTask", taskName + " OnPreExecute")
            }
            /*核心方法2*/
            override fun doInBackground(vararg params: String?): String {

                try {
                    Thread.sleep(5000)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                /*輔助方法*/
                publishProgress(50)

                Log.d("MyAsyncTask", taskName + " doInBackground")

                return taskName
            }
            /*核心方法3*/
            override fun onPostExecute(result: String?) {
                super.onPostExecute(result)
                Log.d("MyAsyncTask",
                        "I am Task" + result + " executed at  " + SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(Date()))

            }
            /*核心方法4*/
            override fun onProgressUpdate(vararg values: Int?) {
                super.onProgressUpdate(*values)

                Log.d("MyAsyncTask", taskName + " onProgressUpdate")
            }
        }
    }

聲明好了靜態內部類MyAsyncTask後,小手一點Button執行異步任務。

/*execute是Button控件的id,這裏沒有使用findViewById()*/
execute!!.setOnClickListener({
     MyAsyncTask("AsyncTask1").execute()
 })

介紹一下上面點擊按鈕執行任務操作,在項目中是沒有寫findViewById()去初始化Button,通過Kotlin Android Extensions拓展插件直接可以使用xml佈局裏Button的id來調用相應的方法,兩個字:簡便。有興趣的同學可以去Kotlin官網瞭解一波。下面是MyAsyncTask執行的結果。

這裏寫圖片描述

根據圖示介紹一下AsyncTask的核心方法

1-2.onPreExecute()

在主線中執行,用於異步任務執行之前做一些準備工作,比如初始化加載進度條之類。

1-3.doInBackground(vararg params: String?)

在子線程中執行,真正執行異步任務的方法,參數是異步任務輸入參數。在此方法中可以通過 publishProgress()方法來更新異步任務當前的進度,調用publishProgress()方法會同步調用onProgressUpdate()

1-4. onProgressUpdate(vararg values: Int?)

在主線程中執行,此方法用來實時顯示當前任務的執行進度。作用是可以在此處更新UI上的進度條進度或者做其他操作。

1-5. onPostExecute(result: String?)

在主線程中執行,異步任務執行結束之後會執行此方法。參數是doInBackground()的返回值。

AsyncTask的核心方法就是上面所述,執行任務是在子線程中,而任務準備工作,進度更新等全是在主線程中完成。AsyncTask實例的創建也是要在主線程中去執行。

二.源碼解析

根據代碼執行順序,我們首先看下AsyncTask的 execute()方法。

/*註解,主線程中執行*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
@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 後臺執行任務所需參數*/
        mWorker.mParams = params;
        /*線程池執行線程任務,mFuture封裝了開啓線程的mWorker*/
        exec.execute(mFuture);

        return this;
    }

我們看下上面executeOnExecutor()方法中幾個重要的屬性。exec →* SerialExecutor*,mWorker → WorkerRunnable和mFuture → FutureTask

WorkerRunnable:任務執行返回的結果

這裏mWorker對象的創建是在AsyncTask的構造函數中,本質是開啓一個線程,call()方法將任務執行完的結果返回。

  mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //標記任務已經被調用過
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //將線程的優先級設置爲後臺,爲了執行完任務方便回收
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //後臺執行任務
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    //有異常將此任務取消
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //任務結束
                    postResult(result);
                }
                return result;
            }
        };

FutureTask:線程任務(理解爲要執行的任務)

這裏將mWorker任務的參數封裝成FutureTask對象,按照中式英語理解的話就是將來要執行的任務。本質是一個Runnable對象。

mFuture = new FutureTask<Result>(mWorker) {
       @Override
       /*任務完成後的回調*/
       protected void done() {
          try {
              /*調用下方postResult()方法*/
               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) {
               /*調用下方postResult()方法*/
               postResultIfNotInvoked(null);
        }
     }
};

當線程任務執行結束後,調用postResult(Result result)通過InternalHandler將線程池(子線程任務)切換到主線程中。

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

getHandler()獲取的就是InternalHandler對象,這是一個靜態內部類。切換線程作用。

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

exec:線程池執行類

execSerialExecutor串行線程池對象,主要作用是將要執行的任務(mFutureTask)通過此類被插入到任務隊列mTasks中,整合任務執行順序作用。會不斷從隊列中取出任務來執行,如果當前任務執行完畢,mTasks會查找隊列頂端是否有mFutureTask對象。有的話就執行。沒有任務結束。

/*源碼中不是這樣寫的,這裏爲了方便理解*/
public static final Executor exec = 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() {
            /*隊列最上方的一個元素不爲null即隊列頂端有空閒的任務*/
            if ((mActive = mTasks.poll()) != null) {
                /*真正執行任務的線程池:THREAD_POOL_EXECUTOR核心類ThreadPoolExecutor對象*/
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

其實真正執行任務的線程池是:ThreadPoolExecutor核心類的對象THREAD_POOL_EXECUTOR。

THREAD_POOL_EXECUTOR.execute(mActive);

目前 AsyncTask在Android3.0以上版本的執行順序是串行的,有個多個任務添加到mTasks隊列中,執行的順序是從隊列頂端往下依次執行。如果想實現多線程的並行任務。只要將

 MyAsyncTask("AsyncTask1").execute()

替換爲下述即可:

 MyAsyncTask("AsyncTask1").executeOnExecutor()

結尾

Android中多線程任務雖然沒有Java中使用頻率高,但是是一個比較重要的知識點。AsyncTask可以在很多多線程任務中發揮好的作用,比如項目中的在線更新等。線程池也是特別重要的知識點,幾乎是面試必問,接下來將會介紹線程池的工作原理等相關知識。

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