編輯:關於Android編程
在之前的博客《Android中AsyncTask使用詳解》中我們提到AsyncTask是對Thread和Handler的組合包裝,本文將通過解析的方式讓大家了解AsyncTask的工作原理。
AsyncTask的源碼鏈接https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/AsyncTask.java
AsyncTask一開始定義了一些字段,如下所示:
private static final String LOG_TAG = "AsyncTask";
//CPU_COUNT為手機中的CPU核數
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//將手機中的CPU核數加1作為AsyncTask所使用的線程池的核心線程數的大小
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//將CPU_COUNT * 2 + 1作為AsyncTask所使用的線程池的最大線程數的大小
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
//實例化線程工廠ThreadFactory,sThreadFactory用於在後面創建線程池
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
//mCount為AtomicInteger類型,AtomicInteger是一個提供原子操作的Integer類,
//確保了其getAndIncrement方法是線程安全的
private final AtomicInteger mCount = new AtomicInteger(1);
//重寫newThread方法的目的是為了將新增線程的名字以"AsyncTask #"標識
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//實例化阻塞式隊列BlockingQueue,隊列中存放Runnable,容量為128
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
//根據上面定義的參數實例化線程池
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
通過以上代碼和注釋我們可以知道,AsyncTask初始化了一些參數,並用這些參數實例化了一個線程池THREAD_POOL_EXECUTOR
,需要注意的是該線程池被定義為public static final
,由此我們可以看出AsyncTask內部維護了一個靜態的線程池,默認情況下,AsyncTask的實際工作就是通過該THREAD_POOL_EXECUTOR
完成的。
我們繼續,在執行完上面的代碼後,AsyncTask又有如下一條語句:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
上面的代碼實例化了一個SerialExecutor類型的實例SERIAL_EXECUTOR
,它也是public static final
的。SerialExecutor是AsyncTask的一個內部類,代碼如下所示:
//SerialExecutor實現了Executor接口中的execute方法,該類用於串行執行任務,
//即一個接一個地執行任務,而不是並行執行任務
private static class SerialExecutor implements Executor {
//mTasks是一個維護Runnable的雙端隊列,ArrayDeque沒有容量限制,其容量可自增長
final ArrayDeque mTasks = new ArrayDeque();
//mActive表示當前正在執行的任務Runnable
Runnable mActive;
public synchronized void execute(final Runnable r) {
//execute方法會傳入一個Runnable類型的變量r
//然後我們會實例化一個Runnable類型的匿名內部類以對r進行封裝,
//通過隊列的offer方法將封裝後的Runnable添加到隊尾
mTasks.offer(new Runnable() {
public void run() {
try {
//執行r的run方法,開始執行任務
//此處r的run方法是在線程池中執行的
r.run();
} finally {
//當任務執行完畢的時候,通過調用scheduleNext方法執行下一個Runnable任務
scheduleNext();
}
}
});
//只有當前沒有執行任何任務時,才會立即執行scheduleNext方法
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//通過mTasks的poll方法進行出隊操作,刪除並返回隊頭的Runnable,
//將返回的Runnable賦值給mActive,
//如果不為空,那麼就讓將其作為參數傳遞給THREAD_POOL_EXECUTOR的execute方法進行執行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
通過以上代碼和注釋我們可以知道:
SerialExecutor實現了Executor接口中的execute方法,該類用於串行執行任務,即一個接一個地執行任務,而不是並行執行任務。
SerialExecutor內部維護了一個存放Runnable的雙端隊列mTasks。當執行SerialExecutor的execute方法時,會傳入一個Runnable變量r,但是mTasks並不直接存儲r,而是又新new了一個匿名Runnable對象,其內部會調用r,這樣就對r進行了封裝,將該封裝後的Runnable對象通過隊列的offer方法入隊,添加到mTasks的隊尾。
SerialExecutor內部通過mActive存儲著當前正在執行的任務Runnable。當執行SerialExecutor的execute方法時,首先會向mTasks的隊尾添加進一個Runnable。然後判斷如果mActive為null,即當前沒有任務Runnable正在運行,那麼就會執行scheduleNext()方法。當執行scheduleNext方法的時候,會首先從mTasks中通過poll方法出隊,刪除並返回隊頭的Runnable,將返回的Runnable賦值給mActive,如果不為空,那麼就讓將其作為參數傳遞給THREAD_POOL_EXECUTOR的execute方法進行執行。由此,我們可以看出SerialExecutor實際上是通過之前定義的線程池THREAD_POOL_EXECUTOR
進行實際的處理的。
當將mTasks中的Runnable作為參數傳遞給THREAD_POOL_EXECUTOR執行execute方法時,會在線程池的工作線程中執行匿名內部類Runnable中的try-finally代碼段,即先在工作線程中執行r.run()方法去執行任務,無論任務r正常完成還是拋出異常,都會在finally中執行scheduleNext方法,用於執行mTasks中的下一個任務。從而在此處我們可以看出SerialExecutor是一個接一個執行任務,是串行執行任務,而不是並行執行。
AsyncTask內部定義了一個Status枚舉類型,如下所示:
public enum Status {
//PENDING表示還沒有開始執行任務
PENDING,
//RUNNING表示已經開始執行任務
RUNNING,
//FINISHED表示任務已經執行完成或被取消了,總之onPostExecute方法已經被調用了
FINISHED,
}
一個AsyncTask正常情況下會經歷PENDING->RUNNING->FINISHED三個狀態。
AsyncTask還定義了以下字段:
//用於通過Handler發布result的Message Code
private static final int MESSAGE_POST_RESULT = 0x1;
//用於通過Handler發布progress的Message Code
private static final int MESSAGE_POST_PROGRESS = 0x2;
//sDefaultExecutor表示AsyncTask默認使用SERIAL_EXECUTOR作為Executor,
//即默認情況下AsyncTask是串行執行任務,而不是並行執行任務
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//InternalHandler是AsyncTask中定義的一個靜態內部類,其綁定了主線程的Looper和消息隊列
private static InternalHandler sHandler;
//mWorker是一個實現了Callable接口的對象,其實現了Callable接口的call方法
private final WorkerRunnable mWorker;
//根據mFuture是一個FutureTask對象,需要用mWorker作為參數實例化mFuture
private final FutureTask mFuture;
//AsyncTask的初始狀態位PENDING
private volatile Status mStatus = Status.PENDING;
//mCancelled標識當前任務是否被取消了
private final AtomicBoolean mCancelled = new AtomicBoolean();
//mTaskInvoked標識當前任務是否真正開始執行了
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
我們對以上代碼再進行一下說明:
sDefaultExecutor表示AsyncTask執行任務時默認所使用的線程池,sDefaultExecutor的初始值為SERIAL_EXECUTOR,表示默認情況下AsyncTask是串行執行任務,而不是並行執行任務。
InternalHandler是AsyncTask中定義的一個靜態內部類,其部分源碼如下所示:
private static class InternalHandler extends Handler {
public InternalHandler() {
//Looper類的getMainLooper方法是個靜態方法,該方法返回主線程的Looper
//此處用主線程的Looper初始化InternalHandler,表示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:
//發布最後結果
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//發布階段性處理結果
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
通過InternalHandler的構造函數我們可以發現,用主線程的Looper初始化了InternalHandler,說明InternalHandler綁定了主線程。後面會討論InternalHandler的handleMessage方法。
由於AsyncTask能夠取消任務,所以AsyncTask使用了FutureTask以及與其相關的Callable,此處對二者簡單進行一下介紹。FutureTask、Callable在Java的並發編程中是比較常見的,可以用來獲取任務執行完之後的返回值,也可以取消線程池中的某個任務。Callable是一個接口,其內部定義了call方法,在call方法內需要編寫代碼執行具體的任務,在這一點上Callable接口與Runnable接口很類似,不過不同的是Runnable的run方法沒有返回值,Callable的call方法可以指定返回值。FutureTask類同時實現了Callable接口和Runnable接口,FutureTask的構造函數中需要傳入一個Callable對象以對其進行實例化。Executor的execute方法接收一個Runnable對象,由於FutureTask實現了Runnable接口,所以可以把一個FutureTask對象傳遞給Executor的execute方法去執行。當任務執行完畢的時候會執行FutureTask的done方法,我們可以在這個方法中寫一些邏輯處理。在任務執行的過程中,我們也可以隨時調用FutureTask的cancel方法取消執行任務,任務取消後也會執行FutureTask的done方法。我們也可以通過FutureTask的get方法阻塞式地等待任務的返回值(即Callable的call方法的返回值),如果任務執行完了就立即返回執行的結果,否則就阻塞式等待call方法的完成。
我們上面對Callable和FutureTask的使用及其作用進行了簡單介紹,我們再回到AsyncTask的代碼中看一下。mFuture是FutureTask類型的對象,mWorker是WorkerRunnable類型的對象,WorkerRunnable是AsyncTask中的一個內部類,代碼如下所示:
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
由此可以看出WorkerRunnable其實就是一個指定了泛型Result的Callable,所以mWorker也就是一個Callable對象。後面會講解mWorker和mFuture的實例化。
mStatus的值為PENDING,表示AsyncTask的初始狀態為未執行狀態。mCancelled標識當前任務是否被取消了,mTaskInvoked標識當前任務是否真正開始執行了。
下面我們看一下AsyncTask的構造函數:
//AsyncTask的構造函數需要在UI線程上調用
public AsyncTask() {
//實例化mWorker,實現了Callable接口的call方法
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
//call方法是在線程池的某個線程中執行的,而不是運行在主線程中
//call方法開始執行後,就將mTaskInvoked設置為true,表示任務開始執行
mTaskInvoked.set(true);
//將執行call方法的線程設置為後台線程級別
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//在線程池的工作線程中執行doInBackground方法,執行實際的任務,並返回結果
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
//將執行完的結果傳遞給postResult方法
return postResult(result);
}
};
//用mWorker實例化mFuture
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
//任務執行完畢或取消任務都會執行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);
}
}
};
}
在AsyncTask的構造函數中依次實例化了mWorker和mFuture,AsyncTask的構造函數需要在UI線程上調用。下面對以上代碼進行一下說明。
mWorker
我們之前提到,mWorker其實是一個Callable類型的對象。實例化mWorker,實現了Callable接口的call方法。call方法是在線程池的某個線程中執行的,而不是運行在主線程中。在線程池的工作線程中執行doInBackground方法,執行實際的任務,並返回結果。當doInBackground執行完畢後,將執行完的結果傳遞給postResult方法。postResult方法我們後面會再講解。
mFuture
mFuture是一個FutureTask類型的對象,用mWorker作為參數實例化了mFuture。在這裡,其實現了FutureTask的done方法,我們之前提到,當FutureTask的任務執行完成或任務取消的時候會執行FutureTask的done方法。done方法裡面的邏輯我們稍後再將。
在實例化了AsyncTask對象之後,我們就可以調用AsyncTask的execute方法執行任務,execute代碼如下所示:
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute方法有@MainThread
,所以該方法應該在主線程上執行。通過上面代碼我們發現,execute方法只是簡單調用了executeOnExecutor方法,並且把sDefaultExecutor作為參數傳遞給了executeOnExecutor,此處就能看出sDefaultExecutor是默認執行任務的Executor了。
我們再看一下executeOnExecutor方法,其代碼如下所示:
@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
//如果當前AsyncTask已經處於運行狀態,那麼就拋出異常,不再執行新的任務
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
//如果當前AsyncTask已經把之前的任務運行完成,那麼也拋出異常,不再執行新的任務
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
//將AsyncTask的狀態置為運行狀態
mStatus = Status.RUNNING;
//在真正執行任務前,先調用onPreExecute方法
onPreExecute();
mWorker.mParams = params;
//Executor的execute方法接收Runnable參數,由於mFuture是FutureTask的實例,
//且FutureTask同時實現了Callable和Runnable接口,所以此處可以讓exec執行mFuture
exec.execute(mFuture);
//最後將AsyncTask自身返回
return this;
}
下面對以上代碼進行一下說明:
一個AsyncTask實例執行執行一次任務,當第二次執行任務時就會拋出異常。executeOnExecutor方法一開始就檢查AsyncTask的狀態是不是PENDING,只有PENDING狀態才往下執行,如果是其他狀態表明現在正在執行另一個已有的任務或者已經執行完成了一個任務,這種情況下都會拋出異常。
如果開始是PENDING狀態,那麼就說明該AsyncTask還沒執行過任何任務,代碼可以繼續執行,然後將狀態設置為RUNNING,表示開始執行任務。
在真正執行任務前,先調用onPreExecute方法。由於executeOnExecutor方法應該運行在主線程上,所以此處的onPreExecute方法也會運行在主線程上,可以在該方法中做一些UI上的處理操作。
Executor的execute方法接收Runnable參數,由於mFuture是FutureTask的實例,且FutureTask同時實現了Callable和Runnable接口,所以此處可以讓exec通過execute方法在執行mFuture。在執行了exec.execute(mFuture)之後,後面會在exec的工作線程中執行mWorker的call方法,我們之前在介紹mWorker的實例化的時候也介紹了call方法內部的執行過程,會首先在工作線程中執行doInBackground方法,並返回結果,然後將結果傳遞給postResult方法。
下面我們看一下postResult方法,代碼如下所示:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
//通過getHandler獲取InternalHandler,InternalHandler綁定主線程
//根據InternalHandler創建一個Message Code為MESSAGE_POST_RESULT的Message
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
//將該message發送給InternalHandler
message.sendToTarget();
return result;
}
在postResult方法中,通過getHandler獲取InternalHandler,InternalHandler綁定主線程。getHandler代碼如下所示:
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
在得到InternalHandler對象之後,會根據InternalHandler創建一個Message Code為MESSAGE_POST_RESULT的Message,此處還將doInBackground返回的result通過new AsyncTaskResult
封裝成了AsyncTaskResult,將其作為message的obj屬性。
AsyncTaskResult是AsyncTask的一個內部類,其代碼如下所示:
//AsyncTaskResult用於發布result或用於發布階段性的數據
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult {
//mTask表示當前AsyncTaskResult是哪個AsyncTask的結果
final AsyncTask mTask;
//mData表示其存儲的數據
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
//此處的data是可變數組
mTask = task;
mData = data;
}
}
在構建了message對象後,通過message.sendToTarget()
將該message發送給InternalHandler,之後InternalHandler的handleMessage方法會接收並處理該message,如下所示:
public void handleMessage(Message msg) {
//msg.obj是AsyncTaskResult類型
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
//發布最後結果
result.mTask.finish(result.mData[0]);
break;
...
result.mTask.onProgressUpdate(result.mData);
break;
}
}
msg.obj是AsyncTaskResult類型,result.mTask表示當前AsyncTaskResult所綁定的AsyncTask。result.mData[0]表示的是doInBackground所返回的處理結果。將該結果傳遞給AsyncTask的finish方法,finish代碼如下所示:
private void finish(Result result) {
if (isCancelled()) {
//如果任務被取消了,那麼執行onCancelled方法
onCancelled(result);
} else {
//將結果發傳遞給onPostExecute方法
onPostExecute(result);
}
//最後將AsyncTask的狀態設置為完成狀態
mStatus = Status.FINISHED;
}
finish方法內部會首先判斷AsyncTask是否被取消了,如果被取消了執行onCancelled(result),否則執行onPostExecute(result)方法。需要注意的是InternalHandler是指向主線程的,所以其handleMessage方法是在主線程中執行的,從而此處的finish方法也是在主線程中執行的,進而onPostExecute也是在主線程中執行的。
上面我們知道了任務從開始執行到onPostExecute的過程。我們知道,doInBackground方法是在工作線程中執行比較耗時的操作,這個操作時間可能比較長,而我們的任務有可能分成多個部分,每當我完成其中的一部分任務時,我們可以在doInBackground中多次調用AsyncTask的publishProgress方法,將階段性數據發布出去。
publishProgress方法代碼如下所示:
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
我們需要將階段性處理的結果傳入publishProgress,其中values是不定長數組,如果任務沒有被取消,就會通過InternalHandler創建一個Message對象,該message的Message Code標記為MESSAGE_POST_PROGRESS,並且會根據傳入的values創建AsyncTaskResult,將其作為message的obj屬性。然後再將該message發送給InternalHandler,然後InternalHandler會執行handleMessage方法,接收並處理該message,如下所示:
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
...
case MESSAGE_POST_PROGRESS:
//發布階段性處理結果
result.mTask.onProgressUpdate(result.mData);
break;
}
}
在handleMessage方法中,當message.what為MESSAGE_POST_PROGRESS時,會執行AsyncTask的onProgressUpdate方法,該方法是在主線程中執行的。
AsyncTask無論任務完成還是取消任務,FutureTask都會執行done方法,如下所示:
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
//任務執行完畢或取消任務都會執行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);
}
}
};
無論任務正常執行完成還是任務取消,都會執行postResultIfNotInvoked方法。postResultIfNotInvoked代碼如下所示:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
//只有mWorker的call沒有被調用才會執行postResult方法
postResult(result);
}
}
如果AsyncTask正常執行完成的時候,call方法都執行完了,mTaskInvoked設置為true,並且在call方法中最後執行了postResult方法,然後進入mFuture的done方法,然後進入postResultIfNotInvoked方法,由於mTaskInvoked已經執行,所以不會執行再執行postResult方法。
如果在調用了AsyncTask的execute方法後立馬就執行了AsyncTask的cancel方法(實際執行mFuture的cancel方法),那麼會執行done方法,且捕獲到CancellationException異常,從而執行語句postResultIfNotInvoked(null)
,由於此時還沒有來得及執行mWorker的call方法,所以mTaskInvoked還未false,這樣就可以把null傳遞給postResult方法。
這樣,我們基本就將AsyncTask中的細節都講到了,希望本文對大家理解AsyncTask的工作原理有所幫助!
關於activity的動畫,相信大家再熟悉不過了,我們開發中經常用到,網上資料也很多,但是也有一些小細節需要我們注意,今天這篇文章我總結了幾種常用的動畫實現方式,通過這篇
小編一直任務將web和android組件結合起來做應用可以事半功倍,html5一來就更有說服力了,特別是對於以前從事web開發的兄弟來說 1. webview加入布局文件
小米手機如何快速截屏:在這裡總結了兩種小米手機快速截屏的方法,不知道或者要用的童鞋還不快點收藏起來試試看!1、下拉通知欄,點擊截屏,2秒後直接進入截屏截取當
在前一章中講的是Android使用HttpURLConnection下載圖片,這一章使用HttpClient下載圖片 HttpURLConnection與HttpClie