編輯:關於Android編程
AsyncTask的一個典型的應用場景是:後台下載文件,並實時跟新下載進度。它既能使得耗時的事情在後台線程中執行,又能和主線程通信,告訴主線程更新UI。同時,AsyncTask內部使用了線程池來執行後台任務,因此它能處理多任務請求。那麼它的內部是怎麼實現的呢?
在閱讀源碼之前,我們還是看一下AsyncTask的使用步驟:
這部分參考了AsyncTask的基本用法 這篇博客。
2.1onPreExecute(), 該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些准備工作,如在界面上顯示一個進度條。
2.2doInBackground(Params…), 將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這裡將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
2.3onProgressUpdate(Progress…),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。
2.4onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後台的計算結果將通過該方法傳遞到UI thread.
使用executeOnExecutor後者execute方法。
同時,我們還需要注意一下幾點:
A)Task的實例必須在UI thread中創建
B) execute方法必須在UI thread中調用
C) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
D) 該task只能被執行一次,否則多次調用時將會出現異常
AsyncTask的源碼不多,因此分析比較容易。我之所以要分析這個類的源碼是因為我遇到了這樣的困惑:既然AsyncTask使用線程池來處理多任務請求,那麼我提交多個任務是不是應該這樣呢?
//1 創建一個AsyncTask 的實例
AsyncTask asyncTask = new XXAsyncTask()
//2 提交多個任務
asyncTask.execute(1);
asyncTask.execute(2);
asyncTask.execute(3);
asyncTask.execute(4);
asyncTask.execute(5);
很不幸的是,程序立刻就掛掉了,問什麼呢?我想的,既然AsyncTask內部有個線程池,那麼我不斷給它提交任務不就可以了嗎?而事實證明這樣不行,那麼正確的姿勢是怎麼樣的呢?
比如要提交10個任務:
for(int i=0;i<10;i++){
//1 創建一個AsyncTask 的實例
AsyncTask asyncTask = new XXAsyncTask()
//2 提交多個任務
asyncTask.execute(i);
}
也就是說要不斷的new AsyncTask 的實例,這是什麼情況呢?難道不是每個AsyncTask都有一個線程池嗎?還真不是的。
打開源碼一看,立刻明白了,AsyncTask使用的是單例模式。
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/**
* 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();
AsyncTask構造了兩個靜態的線程池:THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR 。因為它們是靜態的,因此所有的AsyncTask對象都共享這兩個線程池。也就是說,我已開始的理解就是錯的,我以為一個AsyncTask內部有一個線程池,其實不然,而是所有的對象共享這兩個線程池,其中SERIAL_EXECUTOR 是默認使用的線程池,當然我們可以選擇使用THREAD_POOL_EXECUTOR作為線程池的,主要用到executeOnExecutor方法,比如:
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,1);
理解了這點後,我們看看AsyncTask的具體內部實現。
當我們創建好AsyncTask實例後,采用默認的線程池的情況下,我們會執行excute方法,這個方法如下:
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
調用executeOnExecutor方法進一步處理,注意傳給executeOnExecutor方法的參數sDefaultExecutor定義如下:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
也就是說默認的線程池是SERIAL_EXECUTOR了。
然後我們看一下executeOnExecutor是如何進一步處理的:
@MainThread
public final AsyncTask 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;
}
該方法中出現的Status定義如下:
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中只定義了三種狀態,因此,asyncTask的狀態如果不是Status.PENDING,就會拋出異常。這是為什麼我們向下面這樣使用程序會掛掉:
//1 創建一個AsyncTask 的實例
AsyncTask asyncTask = new XXAsyncTask()
//2 提交多個任務
asyncTask.execute(1);
asyncTask.execute(2);
asyncTask.execute(3);
第一次調用execute的時候,asyncTask的狀態由Status.PENDING轉為Status.RUNNING,第二次調用的是有,asyncTask的狀態還是Status.RUNNING,因此程序就會掛掉了。
接下來會調用onPreExecute()方法,也就是說這個方法是在UI線程中調用的,我們完全可以在其中更新UI。做一個初始化工作,這個時候,我們提交的任務還沒有執行。繼續往下看。
接下來調用了:
exec.execute(mFuture);
exec就是excute方法中傳入的sDefaultExecutor ,它默認初始化為SERIAL_EXECUTOR,SERIAL_EXECUTOR的定義如下:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
}
}
}
exec.excute方法也就是這裡的execute方法了。這裡構造了一個Runnable的實例並把它添加到mTasks 中,第一次執行到這裡mActive 肯定為null,因此會調用scheduleNext方法,這個方法使用 mTasks.poll()去除之前添加的Runable,然後執行THREAD_POOL_EXECUTOR.execute(mActive),從而把Runable添加到線程池中。
Runnable中調用了傳入的Runnable的run方法,這個Runable就是mFuture,mFuture定義如下:
mFuture = new FutureTask(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);
}
}
};
mFuture 是一個FutureTask的實例,並且這個實例接受一個mWork參數,這個mWork是一個和mFuture一樣頂一個AsyncTask的構造方法中:
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mWorker 其實是一個Callable的實例了,他有個call方法。而mFuture本質上是一個Runnable的實例,調用mFuture的run方法其實就是調用FutureTask中的run方法,FutureTask接受了mWorker 作為參數,並把它賦值給自己的callable 屬性,代碼如下:
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
所以我們看看FutureTask的run方法
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
這個方法做了如下事情:
1.調用callable的call方法,也就是mWork的call方法,mWork的call方法step 3中已經貼過,其中會調用doInBackground方法,也就是我們提交的需要在後台執行的代碼了。因此,doInBackground是在mWork的call方法中被執行的,mWork的call方法又是通過THREAD_POOL_EXECUTOR.execute(mActive)被添加到線程池後被執行的。
2.調用postResult方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
postResult方法會發送一個消息,這個消息會使得InternalHandler的handleMessage方法被調用。這個時候,我們已經離開了後台線程,又回到UI線程了。因為InternalHandler的Looper是UI線程的Looper,其構造函數中有如下代碼可以知曉:
public InternalHandler() {
super(Looper.getMainLooper());
}
注意我們獲取的消息為MESSAGE_POST_RESULT,因此根據handleMessage的定義:
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;
}
}
會調用到AsyncTask的finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
這個方法中,我們沒有調用取消方法的話,就會調用onPostExecute方法了,這樣,我們使用AsyncTask需要覆寫的幾個方法只有onProgressUpdate方法沒有調用了。那麼這個方法是怎麼被調用的呢?我們說,我們要更新進度的話,需要使用publishProgress方法,我們看看這個方法:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
這個方法就是發送一個消息,然後還是InternalHandler方法handleMessage方法被調用,注意這會消息是MESSAGE_POST_PROGRESS,因此根據源碼,再貼一次handleMessage方法吧:
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;
}
}
可見這次調用的是onProgressUpdate方法。至此,所有需要覆寫的方法的調用過程我們都分析結束了。
ListView上拉加載和下拉刷新多種實現方式
該篇為ListView下拉刷新和上拉加載實現的各種方法大合集。可能在具體的細節邏輯上處理不太到位,但基本上完成邏輯的實現。細節方面,個人可以根據自己的需求進行完善。該博客
Android中用Bmob實現短信驗證碼功能的方法詳解
這篇文章主要介紹發送驗證碼和校驗驗證碼的功能,用到一個第三方平台Bmob,那Bmob是什麼呢?Bmob可以開發一個雲存儲的移動應用軟件,他提供了大量的標准的A
Android開發中Activity的生命周期及加載模式詳解
本文給大家介紹Activity的生命周期,如果大家學習過iOS的小伙伴的話,Activity的生命周期和iOS中ViewController的生命周期非常類似。生命周期,
Android NDK開發總結
1,搭建本地NDK環境 Build path中設置C/C++ build Build command ndk-build NDK_DEBUG=1C/C++ General