Android Handler機制系列文章整體內容如下:
- Android Handler機制1之Thread
- Android Handler機制2之ThreadLocal
- Android Handler機制3之SystemClock類
- Android Handler機制4之Looper與Handler簡介
- Android Handler機制5之Message簡介與消息對象對象池
- Android Handler機制6之MessageQueue簡介
- Android Handler機制7之消息發(fā)送
- Android Handler機制8之消息的取出與消息的其他操作
- Android Handler機制9之Handler的Native實現(xiàn)前奏之Linux IO多路復用
- Android Handler機制10之Handdler的Native實現(xiàn)Native的實現(xiàn)
- Android Handler機制11之Handler機制總結
- Android Handler機制12之Callable、Future和FutureTask
- Android Handler機制13之AsyncTask源碼解析
本篇文章的主要內容如下:
- 1、AsyncTask概述
- 2、基本用法
- 3、AsyncTask類源碼解析
- 4、AsyncTask類的構造函數(shù)
- 5、AsyncTask類核心方法解析
- 6、AsyncTask核心流程
- 7、AsyncTask與Handler
一、AsyncTask概述
我們都知道,Android UI線程是不安全的,如果想要在子線程里面進行UI操作,就需要直接Android的異步消息處理機制,前面我寫了很多文章從源碼層面分析了Android異步消息Handler的處理機制。感興趣的可以去了解下。不過為了更方便我們在子線程中更新UI元素,Android1.5版本就引入了一個AsyncTask類,使用它就可以非常靈活方便地從子線程切換到UI線程。
二、基本用法
AsyncTask是一個抽象類,我們需要創(chuàng)建子類去繼承它,并且重寫一些方法。AsyncTask接受三個泛型的參數(shù):
- Params:指定傳給任務執(zhí)行時的參數(shù)的類型
- Progress:指定后臺任務執(zhí)行時將任務進度返回給UI線程的參數(shù)類型
- Result:指定任務完成后返回的結果類型
除了指定泛型參數(shù),還需要根據(jù)重寫一些方法,常用的如下:
- onPreExecute():這個方法在UI線程調用,用于在任務執(zhí)行器那做一些初始化操作,如在界面上顯示加載進度空間
- onInBackground:在onPreExecute()結束之后立刻在后臺線程調用,用于耗時操作。在這個方法中可調用publishProgress方法返回任務的執(zhí)行進度。
- onProgressUpdate:在doInBackground調用publishProgress后被調用,工作在UI線程
- onPostExecute:后臺任務結束后被調用,工作在UI線程。
三、AsyncTask類源碼解析
(一)、類注釋翻譯
源碼注釋如下:
/**
* <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
* to perform background operations and publish results on the UI thread without
* having to manipulate threads and/or handlers.</p>
*
* <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
* and does not constitute a generic threading framework. AsyncTasks should ideally be
* used for short operations (a few seconds at the most.) If you need to keep threads
* running for long periods of time, it is highly recommended you use the various APIs
* provided by the <code>java.util.concurrent</code> package such as {@link Executor},
* {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
*
* <p>An asynchronous task is defined by a computation that runs on a background thread and
* whose result is published on the UI thread. An asynchronous task is defined by 3 generic
* types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
* and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
* <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using tasks and threads, read the
* <a href="{@docRoot}guide/components/processes-and-threads.html">Processes and
* Threads</a> developer guide.</p>
* </div>
*
* <h2>Usage</h2>
* <p>AsyncTask must be subclassed to be used. The subclass will override at least
* one method ({@link #doInBackground}), and most often will override a
* second one ({@link #onPostExecute}.)</p>
*
* <p>Here is an example of subclassing:</p>
* <pre class="prettyprint">
* 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");
* }
* }
* </pre>
*
* <p>Once created, a task is executed very simply:</p>
* <pre class="prettyprint">
* new DownloadFilesTask().execute(url1, url2, url3);
* </pre>
*
* <h2>AsyncTask's generic types</h2>
* <p>The three types used by an asynchronous task are the following:</p>
* <ol>
* <li><code>Params</code>, the type of the parameters sent to the task upon
* execution.</li>
* <li><code>Progress</code>, the type of the progress units published during
* the background computation.</li>
* <li><code>Result</code>, the type of the result of the background
* computation.</li>
* </ol>
* <p>Not all types are always used by an asynchronous task. To mark a type as unused,
* simply use the type {@link Void}:</p>
* <pre>
* private class MyTask extends AsyncTask<Void, Void, Void> { ... }
* </pre>
*
* <h2>The 4 steps</h2>
* <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
* <ol>
* <li>{@link #onPreExecute()}, invoked on the UI thread before the task
* is executed. This step is normally used to setup the task, for instance by
* showing a progress bar in the user interface.</li>
* <li>{@link #doInBackground}, invoked on the background thread
* immediately after {@link #onPreExecute()} finishes executing. This step is used
* to perform background computation that can take a long time. The parameters
* of the asynchronous task are passed to this step. The result of the computation must
* be returned by this step and will be passed back to the last step. This step
* can also use {@link #publishProgress} to publish one or more units
* of progress. These values are published on the UI thread, in the
* {@link #onProgressUpdate} step.</li>
* <li>{@link #onProgressUpdate}, invoked on the UI thread after a
* call to {@link #publishProgress}. The timing of the execution is
* undefined. This method is used to display any form of progress in the user
* interface while the background computation is still executing. For instance,
* it can be used to animate a progress bar or show logs in a text field.</li>
* <li>{@link #onPostExecute}, invoked on the UI thread after the background
* computation finishes. The result of the background computation is passed to
* this step as a parameter.</li>
* </ol>
*
* <h2>Cancelling a task</h2>
* <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
* this method will cause subsequent calls to {@link #isCancelled()} to return true.
* After invoking this method, {@link #onCancelled(Object)}, instead of
* {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
* returns. To ensure that a task is cancelled as quickly as possible, you should always
* check the return value of {@link #isCancelled()} periodically from
* {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
*
* <h2>Threading rules</h2>
* <p>There are a few threading rules that must be followed for this class to
* work properly:</p>
* <ul>
* <li>The AsyncTask class must be loaded on the UI thread. This is done
* automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
* <li>The task instance must be created on the UI thread.</li>
* <li>{@link #execute} must be invoked on the UI thread.</li>
* <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
* {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
* <li>The task can be executed only once (an exception will be thrown if
* a second execution is attempted.)</li>
* </ul>
*
* <h2>Memory observability</h2>
* <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
* operations are safe without explicit synchronizations.</p>
* <ul>
* <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
* in {@link #doInBackground}.
* <li>Set member fields in {@link #doInBackground}, and refer to them in
* {@link #onProgressUpdate} and {@link #onPostExecute}.
* </ul>
*
* <h2>Order of execution</h2>
* <p>When first introduced, AsyncTasks were executed serially on a single background
* thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
* to a pool of threads allowing multiple tasks to operate in parallel. Starting with
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
* thread to avoid common application errors caused by parallel execution.</p>
* <p>If you truly want parallel execution, you can invoke
* {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
* {@link #THREAD_POOL_EXECUTOR}.</p>
*/
簡單翻譯一下:
- AsyncTask可以輕松的正確使用UI線程,該類允許你執(zhí)行后臺操作并在UI線程更新結果,從而避免了無需使用Handler來操作線程
- AsyncTask是圍繞Thread與Handler而設計的輔助類,所以它并不是一個通用的線程框架。理想情況下,AsyncTask應該用于短操作(最多幾秒)。如果你的需求是在長時間保持線程運行,強烈建議您使用由
java.util.concurrent提供的各種API包,比如Executor、ThreadPoolExecutor或者FutureTask。- 一個異步任務被定義為:在后臺線程上運行,并在UI線程更新結果。一個異步任務通常由三個類型:Params、Progress和Result。以及4個步驟:onPreExecute、doInBackground、onProgressUpdate、onPostExecute。
- 用法:AsyncTask必須由子類實現(xiàn)后才能使用,它的子類至少重寫doInBackground()這個方法,并且通常也會重寫onPostExecute()這個方法
- 下面是一個繼承AsyncTask類的一個子類的例子
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"); } }一旦創(chuàng)建,任務會被很輕松的執(zhí)行。就像下面這塊代碼一樣
new DownloadFilesTask().execute(url1,url2,url3);
- AsyncTask泛型,在異步任務中通常有三個泛型
- Params:發(fā)送給任務的參數(shù)類型,這個類型會被任務執(zhí)行
- Progress:后臺線程進度的計算的基本單位。
- Result:后臺線程執(zhí)行的結果類型。
- 如果異步任務不需要上面類型,則可以需要聲明類型未使用,通過使用Void來表示類型未使用。就像下面一楊
private class MyTask extends AsyncTask<Void, Void, Void>{...}
- 四個步驟
- onPreExecute() 在執(zhí)行任務之前在UI線程上調用,此步驟通常用于初始化任務,例如在用戶界面顯示進度條。
- doInBackground() 方法在 onPreExecute()執(zhí)行完成后調用的。doInBackground()這個方法用于執(zhí)行可能需要很長時間的首臺計算。異步任務的參數(shù)被傳遞到這個步驟中。計算結果必須經(jīng)由這個步驟返回。這個步驟內還可以調用publishProgress()方法發(fā)布一個或者多個進度單位。在調用onProgressUpdate()函數(shù)后,這個結果將在UI線程上更新。
- onProgressUpdate()方法:在調用publishProgress()之后在UI線程上調用。具體的執(zhí)行時間不確定,該方法用于在后臺計算的異步任務,把具體的進度顯示在用戶界面。例如,它可以用于對進度條進行動畫處理或者在文本字段中顯示日志。
- onPostExecute()方法, 后臺任務完成后,在UI線程調用onPostExecute()方法,后臺運行的結果作為參數(shù)傳遞給這個方法
- 取消任務
在任何時候都可以通過調用cancel(boolean)來取消任務。調用此方法將導致isCancelled()方法的后續(xù)調用返回true。調用此方法后,在執(zhí)行doInBackground(Object [])方法后,將調用onCancelled(object),而不是onPostExecute(Object)方法。為了盡可能快的取消任務,如果可能的話,你應該在調用doInBackground(Object[])之前檢查isCancelled()的返回值。- 線程規(guī)則,這個類必須遵循有關線程的一些規(guī)則才能正常使用,規(guī)則如下:
- 必須在UI線程上加載AsyncTask類,android4.1上自動完成
- 任務實例必須在UI線程上實例化
- execute()方法必須在主線程上調用
- 不要手動去調用onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()方法。
- 這個任務只能執(zhí)行一次(如果嘗試第二次執(zhí)行,將會拋出異常)。
該任務只能執(zhí)行一次(如果嘗試第二次執(zhí)行,將拋出異常)。- 內存的觀察AsyncTask。保證所有回調調用都是同步的,使得以下操作在沒有顯示同步情況下是安全的。
- 在構造函數(shù)或者onPreExecute設置成員變量,并且在doInBackground()方法中引用它們。
- 在doInBackground()設置成員字段,并在onProgressUpdate()和onPostExecute()方法中引用他們。
- 執(zhí)行順序。第一引入AsyncTask時,AsyncTasks是在單個后臺線程串行執(zhí)行的。在android1.6以后,這被更改為允許多個任務并行操作的線程池。從android 3.0開始,每個任務都是執(zhí)行在一個獨立的線程上,這樣可以避免一些并行執(zhí)行引起的常見的應用程序錯誤。如果你想要并行執(zhí)行,可以使用THREAD_POOL_EXECUTOR來調用executeOnExecutor()方法。
至此這個類的注釋翻譯完畢,好長啊,大家看完翻譯,是不是發(fā)現(xiàn)了很多之前沒有考慮到的問題。
(二)、AsyncTask的結構
AsyncTask的結構如下:

我們看到在AsyncTask有4個自定義類,一個枚舉類,一個靜態(tài)塊,然后才是這個類的具體變量和屬性,那我們就依次講解
(三)、枚舉Status
代碼在AsyncTask.java 256行
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
枚舉Status上的注釋翻譯一下就是:
Status表示當前任務的狀態(tài),每種狀態(tài)只能在任務的生命周期內設置一次。
所以任務有三種狀態(tài)
- PENDING:表示任務尚未執(zhí)行的狀態(tài)
- RUNNING:表示任務正在執(zhí)行
- FINISHED:任務已完成
(四)、私有的靜態(tài)類InternalHandler
代碼在AsyncTask.java 656行
private static class InternalHandler extends Handler {
public InternalHandler() {
// 這個handler是關聯(lián)到主線程的
super(Looper.getMainLooper());
}
@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;
}
}
}
通過上面的代碼我們知道:
- InternalHandler繼承自Handler,并且在它的構造函數(shù)里面調用了Looper.getMainLooper(),所以我們知道這個Handler是關聯(lián)主線程的。
- 重寫了handleMessage(Message)方法,其中這個Message的obj這個類型是AsyncTaskResult(AsyncTaskResult我將在下面講解),然后根據(jù)msg.what的來區(qū)別。
- 我們知道這個Message只有兩個標示,一個是MESSAGE_POST_RESULT代表消息的結果,一個是MESSAGE_POST_PROGRESS代表要執(zhí)行onProgressUpdate()方法。
通過這段代碼我們可以推測AsyncTask內部實現(xiàn)線程切換,即切換到主線程是通過Handler來實現(xiàn)的。
(五)、私有的靜態(tài)類AsyncTaskResult
代碼在AsyncTask.java 682行
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
通過類名,我們大概可以推測出這一個負責AsyncTask結果的類
AsyncTaskResult這個類 有兩個成員變量,一個是AsyncTask一個是泛型的數(shù)組。
- mTask參數(shù):是為了AsyncTask是方便在handler的handlerMessage回調中方便調用AsyncTask的本身回調函數(shù),比如onPostExecute()函數(shù)、onPreogressUpdata()函數(shù),所以在AsyncTaskResult需要持有AsyncTask。
- mData參數(shù):既然是代表結果,那么肯定要有一個變量持有這個計算結果
(六)、私有靜態(tài)抽象類WorkerRunnable
代碼在AsyncTask.java 677行
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
這個抽象類很簡答,首先是實現(xiàn)了Callable接口,然后里面有個變量
mParams,類型是泛型傳進來的數(shù)組
(七)、局部變量詳解
AsyncTask的局部變量如下:
private static final String LOG_TAG = "AsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
那我們就來一一解答
- String LOG_TAG = "AsyncTask":打印專用
- CPU_COUNT = Runtime.getRuntime().availableProcessors():獲取當前CPU的核心數(shù)
- CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)):線程池的核心容量,通過代碼我們知道是一個大于等于2小于等于4的數(shù)
- MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1:線程池的最大容量是2倍的CPU核心數(shù)+1
- KEEP_ALIVE_SECONDS = 30:過剩的空閑線程的存活時間,一般是30秒
- sThreadFactory:線程工廠,通過new Thread來獲取新線程,里面通過使用AtomicInteger原子整數(shù)保證超高并發(fā)下可以正常工作。
- sPoolWorkQueue:靜態(tài)阻塞式隊列,用來存放待執(zhí)行的任務,初始容量:128個
- THREAD_POOL_EXECUTOR:線程池
- SERIAL_EXECUTOR = new SerialExecutor():靜態(tài)串行任務執(zhí)行器,其內部實現(xiàn)了串行控制,循環(huán)的取出一個個任務交給上述的并發(fā)線程池去執(zhí)行。
- MESSAGE_POST_RESULT = 0x1:消息類型,代表發(fā)送結果
- MESSAGE_POST_PROGRESS = 0x2:消息類型,代表進度
- sDefaultExecutor = SERIAL_EXECUTOR:默認任務執(zhí)行器,被賦值為串行任務執(zhí)行器,就是因為它,AsyncTask變成了串行的了。
- sHandler:靜態(tài)Handler,用來發(fā)送上面的兩種通知,采用UI線程的Looper來處理消息,這就是為什么AnsyncTask可以在UI線程更新UI
- WorkerRunnable<Params, Result> mWorke:是一個實現(xiàn)Callback的抽象類,擴展了Callable多了一個Params參數(shù)。
- mFuture:FutureTask對象
- mStatus = Status.PENDING:任務的狀態(tài)默認為掛起,即等待執(zhí)行,其類型標示為volatile
- mCancelled = new AtomicBoolean():原子布爾類型,支持高并發(fā)訪問,標示任務是否被取消
- mTaskInvoked = new AtomicBoolean():原子布爾類型,支持高并發(fā)訪問,標示任務是否被執(zhí)行過
(八)、靜態(tài)代碼塊
代碼在AsyncTask.java 226行
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;
}
通過上面的(七)、局部變量詳解,我們知道在靜態(tài)代碼塊中創(chuàng)建了一個線程池threadPoolExecutor,并設置了核心線程會超時關閉,最后并把這個線程池指向THREAD_POOL_EXECUTOR。
(九)、私有的靜態(tài)類SerialExecutor
代碼在AsyncTask.java 226行
private static class SerialExecutor implements Executor {
// 循環(huán)數(shù)組實現(xiàn)的雙向Queue,大小是2的倍數(shù),默認是16,有隊頭和隊尾巴兩個下標
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
// 正在運行runnable
Runnable mActive;
public synchronized void execute(final Runnable r) {
// 添加到雙向隊列中去
mTasks.offer(new Runnable() {
public void run() {
try {
//執(zhí)行run方法
r.run();
} finally {
//無論執(zhí)行結果如何都會取出下一個任務執(zhí)行
scheduleNext();
}
}
});
// 如果沒有活動的runnable,則從雙端隊列里面取出一個runnable放到線程池中運行
// 第一個請求任務過來的時候mActive是空的
if (mActive == null) {
//取出下一個任務來
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//從雙端隊列中取出一個任務
if ((mActive = mTasks.poll()) != null) {
//線線程池執(zhí)行取出來的任務,真正的執(zhí)行任務
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
- 首先,注意SerialExecutor的execute是synchronized的,所以無論多個少任務調用execute()都是同步的。
- 其次,SerialExecutor里面一個ArrayDeque隊列,通過代碼我們知道,SerialExecutor是通過ArrayDeque來管理Runnable對象的。通過上面我們知道execute()是同步的,所以如果你有10個任務同時調用SerialExecutor的execute()方法,就會把10個Runnable先后放入到mTasks中去,可見mTasks緩存了將要執(zhí)行的Runnable。
- 再次1,如果我們第一個執(zhí)行execute()方法時,會調用ArrayDeque的offer()方法將傳入的Runnable對象添加到隊列的尾部,然后判斷mActive是不是null,因為是第一次調用,此時mActive還沒有賦值,所以mActive等于null。所以此時mActive == null成立,所以會調用scheduleNext()方法。
- 再次2,我們在調用scheduleNext()里面,看到會調用mTasks.poll(),我們知道這是從隊列中取出頭部元素,然后把這個頭部元素賦值給mActive,然后讓THREAD_POOL_EXECUTOR這個線程池去執(zhí)行這個mActive的Runnable對象。
- 再次3,如果這時候有第二個任務入隊,但是此時mActive!=null,不會執(zhí)行scheduleNext(),所以如果第一個任務比較耗時,后面的任務都會進入隊列等待。
- 再次4,上面知道由于第二個任務入隊后,由于mActive!=null,所以不會執(zhí)行scheduleNext(),那樣這樣后面的任務豈不是永遠得不到處理,當然不是,因為在offer()方法里面?zhèn)魅胍粋€Runnable的匿名類,并且在此使用了finnally代碼,意味著無論發(fā)生什么情況,這個finnally里面的代碼一定會執(zhí)行,而finnally代碼塊里面就是調用了scheduleNext()方法,所以說每當一個任務執(zhí)行完畢后,下一個任務才會執(zhí)行。
- 最后,SerialExecutor其實模仿的是單一線程池的效果,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執(zhí)行,其余的均處于等待狀態(tài)。
PS:scheduleNext()方法是synchronized,所以也是同步的
重點補充:
在Android 3.0 之前是并沒有SerialExecutor這個類的,那個時候是直接在AsyncTask中構建一個sExecutor常量,并對線程池總大小,同一時刻能夠運行的線程數(shù)做了規(guī)定,代碼如下:
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
四、AsyncTask類的構造函數(shù)
代碼如下:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
// 設置方法已經(jīng)被調用
mTaskInvoked.set(true);
// 設定結果變量
Result result = null;
try {
//設置線程優(yōu)先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//執(zhí)行任務
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
// 產(chǎn)生異常則設置失敗
mCancelled.set(true);
throw tr;
} finally {
// 無論執(zhí)行成功還是出現(xiàn)異常,最后都會調用PostResult
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);
}
}
};
}
通過注釋我們知道,這個方法創(chuàng)建一個異步任務,構造函數(shù)必須在UI線程調用
這里面設計了兩個概念Callable和FutureTask,如果大家對這兩個類有疑問,可以看我上一篇文章Android Handler機制12之Callable、Future和FutureTask
構造函數(shù)也比較簡單,主要就是給mWorker和mFuture初始化,其中WorkerRunnable實現(xiàn)了Callable接口,
在構造函數(shù)里面調用了postResult(Result)和postResultIfNotInvoked(Result),那我們就來分別看下
1、postResult(Result)方法
// doInBackground執(zhí)行完畢,發(fā)送消息
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
// 獲取一個Message對象
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
// 發(fā)送給線程
message.sendToTarget();
return result;
}
通過代碼我們知道
- 生成一個Message
- 把這個Message發(fā)送出
這里面調用了 getHandler(),那我們來看下這個方法是怎么寫的
2、getHandler()方法
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
我們看到返回的是InternalHandler對象,上面說過了InternalHandler其實是關聯(lián)主線程的,所以上面方法 message.sendToTarget(); 其實是把消息發(fā)送給主線程。
大家注意一下 這里的Message的what值為MESSAGE_POST_RESULT,我們來看下InternalHandler遇到InternalHandler這種消息是怎么處理的
private static class InternalHandler extends Handler {
...
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;
...
}
}
}
我們看到MESSAGE_POST_RESULT對應的是指是執(zhí)行AsyncTask的finish(Result)方法,所以我們可以這樣說,無論AsyncTask是成功了還是失敗了,最后都會執(zhí)行finish(Result)方法。那我們來看下finish(Result)方法里面都干了什么?
2.1、finish(Result result) 方法
private void finish(Result result) {
if (isCancelled()) {
// 如果消息取消了,執(zhí)行onCancelled方法
onCancelled(result);
} else {
// 如果消息沒有取消,則執(zhí)行onPostExecute方法
onPostExecute(result);
}
// 設置狀態(tài)值
mStatus = Status.FINISHED;
}
注釋寫的很清楚了,我這里就不說明了,通過上面的代碼和finish方法的分析,我們知道無論成功還是失敗,最后一定會調用finish(Result)方法,所以最后狀態(tài)的值為FINISHED。
3、postResultIfNotInvoked(Result)方法
private void postResultIfNotInvoked(Result result) {
// 獲取mTaskInvoked的值
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
通過上面代碼我們知道,如果mTaskInvoked不為true,則執(zhí)行postResult,但是在mWorker初始化的時候為true,除非在沒有執(zhí)行call方法時候,如果沒有執(zhí)行call,說明這個異步線程還沒有開始執(zhí)行,這個時候mTaskInvoked為false。而這時候調用postResultIfNotInvoked則還是會執(zhí)行postResult(Result),這樣保證了AsyncTask一定有返回值。
五、AsyncTask類核心方法解析
(一)、void onPreExecute()
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
// 在調用doInBackground()方法之前,跑在主線程上
@MainThread
protected void onPreExecute() {
}
其實注釋很清楚了,在task任務開始執(zhí)行的時候在主線程調用,在doInBackground(Params… params) 方法之前調用。
(二)、AsyncTask<Params, Progress, Result> onPreExecute() 方法
/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
* <p>Note: this function schedules the task on a queue for a single background
* thread or pool of threads depending on the platform version. When first
* introduced, AsyncTasks were executed serially on a single background thread.
* Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
* to a pool of threads allowing multiple tasks to operate in parallel. Starting
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
* executed on a single thread to avoid common application errors caused
* by parallel execution. If you truly want parallel execution, you can use
* the {@link #executeOnExecutor} version of this method
* with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
* on its use.
*
* <p>This method must be invoked on the UI thread.
*
* @param params The parameters of the task.
*
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
* @see #execute(Runnable)
*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
首先來翻譯一下注釋
- 使用指定的參數(shù)來執(zhí)行任務,這個方法的返回值是this,也就是它自己,因為這樣設計的目的是可以保持對它的引用。
- 注意:它的調度模式是不同的,一種是單個后臺線程,一種是通過線程池來實現(xiàn),具體那種模式是根據(jù)android版本的不同而不同,當最開始引入AsyncTask的時候,AsyncTask是單個后臺線程上串行執(zhí)行,從Android DONUT 開始,模式變更為通過線程池多任務并行執(zhí)行。在Android HONEYCOMB開始,又變回了在單個線程上執(zhí)行,這樣可以避免并行執(zhí)行的錯誤。如果你還是想并行執(zhí)行,你可以使用executeOnExecutor()方法并且第一個參數(shù)是THREAD_POOL_EXECUTOR就可以了,不過,請注意有關使用警告。
- 必須在UI主線程上調用此方法。
通過代碼我們看到,它的內部其實是調用executeOnExecutor(Executor exec, Params... params)方法,只不過第一個參數(shù)傳入的是sDefaultExecutor,而sDefaultExecutor是SerialExecutor的對象。上面我們提到了SerialExecutor里面利用ArrayDeque來實現(xiàn)串行的,所以我們可以推測出如果在executeOnExecutor(Executor exec, Params... params)方法里面如果第一個參數(shù)是自定義的Executor,AsyncTask就可以實現(xiàn)并發(fā)執(zhí)行。
(三)、executeOnExecutor(Executor exec, Params... params) 方法
/**
* Executes the task with the specified parameters. The task returns
* itself (this) so that the caller can keep a reference to it.
*
* <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
* allow multiple tasks to run in parallel on a pool of threads managed by
* AsyncTask, however you can also use your own {@link Executor} for custom
* behavior.
*
* <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
* a thread pool is generally <em>not</em> what one wants, because the order
* of their operation is not defined. For example, if these tasks are used
* to modify any state in common (such as writing a file due to a button click),
* there are no guarantees on the order of the modifications.
* Without careful work it is possible in rare cases for the newer version
* of the data to be over-written by an older one, leading to obscure data
* loss and stability issues. Such changes are best
* executed in serial; to guarantee such work is serialized regardless of
* platform version you can use this function with {@link #SERIAL_EXECUTOR}.
*
* <p>This method must be invoked on the UI thread.
*
* @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a
* convenient process-wide thread pool for tasks that are loosely coupled.
* @param params The parameters of the task.
*
* @return This instance of AsyncTask.
*
* @throws IllegalStateException If {@link #getStatus()} returns either
* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
*
* @see #execute(Object[])
*/
@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)");
}
}
//設置狀態(tài)
mStatus = Status.RUNNING;
從這里我們看出onPreExecute是先執(zhí)行,并且在UI線程
onPreExecute();
// 設置參數(shù)
mWorker.mParams = params;
// 開啟了后臺線程去計算,這是真正調用doInBackground的地方
exec.execute(mFuture);
// 接著會有onProgressUpdate會被調用,最后是onPostExecute
return this;
}
老規(guī)矩 先翻譯一下注釋:
- 使用指定的參數(shù)來執(zhí)行任務,這個方法的返回值是this,也就是它自己,因為這樣設計的目的是可以保持對它的引用。
- 這個方法通常與THREAD_POOL_EXECUTOR一起使用,這樣可以讓多個人物在AsyncTask管理的線程池上并行運行,但你也可以使用自定義的Executor。
- 警告:由于大多數(shù)情況并沒有定義任務的操作順序,所以在線程池中多任務并行并不常見。例如:如果修改共同狀態(tài)的任務(就像點擊按鈕就可以編寫文件),對修改的順訊沒有保證。在很少的情況下,如果沒有仔細工作,較新版本的數(shù)據(jù)可能會被較舊的數(shù)據(jù)覆蓋,從而導致數(shù)據(jù)丟失和穩(wěn)定性問題。而這些變更最好是連續(xù)執(zhí)行,因為這樣可以保證工作的有序化,無論平臺版本如何,你可以使用SERIAL_EXECUTOR。
- 必須在UI主線程上調用此方法。
- 參數(shù)exec:為了實現(xiàn)輕松解耦,我們可以使用THREAD_POOL_EXECUTOR這個線程池可以作為合適的進程范圍的線程池
- 參數(shù)params:任務的參數(shù)
那我們來看下一下代碼,代碼里面的邏輯如下:
- 該方法首先是判斷mStatus狀態(tài),如果是正在運行(RUNNING)或者已經(jīng)結束(FINISHED),就會拋出異常。
- 接著設置狀態(tài)為RUNNING,即運行,執(zhí)行onPreExecute()方法,并把參數(shù)的值賦給mWorker.mParams
- 于是Executor去執(zhí)行execute的方法,學過Java多線程的都知道,這里方法是開啟一個線程去執(zhí)行mFuture的run()方法(由于mFuture用Callable構造,所以其實是執(zhí)行的Callable的call()方法,而mWorker是Callable的是實現(xiàn)類,所以最終執(zhí)行的是mWorker的call()方法)
PS:mFuture和mWorker都是在AsyncTask的構造方法中初始化過的。
(四)、 publishProgress(Progress... values) 方法
主要是設置后臺進度,onProgressUpdate會被調用
/**
* This method can be invoked from {@link #doInBackground} to
* publish updates on the UI thread while the background computation is
* still running. Each call to this method will trigger the execution of
* {@link #onProgressUpdate} on the UI thread.
*
* {@link #onProgressUpdate} will not be called if the task has been
* canceled.
*
* @param values The progress values to update the UI with.
*
* @see #onProgressUpdate
* @see #doInBackground
*/
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
這個方法內部實現(xiàn)很簡單
- 首先判斷 任務是否已經(jīng)被取消,如果已經(jīng)被取消了,則什么也不做
- 如果任務沒有被取消,則通過InternalHandler發(fā)送一個what為MESSAGE_POST_PROGRESS的Message
這樣就進入了InternalHandler的handleMessage(Message)里面了,而我們知道InternalHandler的Looper是Looper.getMainLooper(),所以處理Message是在主線程中,我們來看下代碼
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_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
通過代碼,我們看到如果what為MESSAGE_POST_PROGRESS,則會在主線程中調用onProgressUpdate(result.mData),這也就是為什么我們平時在異步線程調用publishProgress(Progress...)方法后,可以在主線程中的onProgressUpdate(rogress... values)接受數(shù)據(jù)了。
六、AsyncTask核心流程
其實在上面講解過程中,我基本上已經(jīng)把整體流程講解過了,我這里補上一張圖,比較全面的闡述了AsyncTask的執(zhí)行流程如下:

對應的時序圖如下:

大家如果手機上看不清,我建議down下來在電腦上看。
如果結合AsyncTask的狀態(tài)值,流程圖如下:

如果把AsyncTask和Handler分開則流程圖如下:

最后如果把AsyncTask里面所有類的涉及關系整理如下圖:

七、AsyncTask與Handler
- AsyncTask:
- 優(yōu)點:AsyncTask是一個輕量級的異步任務處理類,輕量級體現(xiàn)在,使用方便,代碼簡潔,而且整個異步任務的過程可以通過cancel()進行控制
- 缺點:不適用處理長時間的異步任務,一般這個異步任務的過程最好控制在幾秒以內,如果是長時間的異步任務就需要考慮多線程的控制問題;當處理多個異步任務時,UI更新變得困難。
- Handler:
- 優(yōu)點:代碼結構清晰,容易處理多個異步任務
- 缺點:當有多個異步任務時,由于要配合Thread或Runnable,代碼可能會稍顯冗余。
總之,AsyncTask不失為一個非常好用的異步任務處理類。不過我從事Android開發(fā)5年多了,很少會用到AsyncTask,一般異步任務都是Handler。