在平常開發中執行非同步操作很常見的操作,經常在執行非同步任務後需要去重新整理 UI,這就需要用到 Android 的非同步訊息處理機制 Handler, 當然我們也可以使用 AsyncTask 去完成,相信大家或多或少都用過 AsyncTask 。
基本用法:
三個泛型 : AsyncTask < Params, Progress, Result >
1. Params : 啟動任務執行傳入的引數, 例如HTTP請求的URL , 執行任務需要的資料型別
2. Progress : 後臺執行任務進度的進度型別
3. Result : 非同步任務最終返回的結果型別
複製程式碼
幾個常用方法:
-
protected void onPreExecute() {}
- 執行在UI執行緒, 一般在執行非同步任務前做一些UI操作
-
protected Result doInBackground(Params ... params) {}
- 執行在非同步執行緒, 耗時的操作在這個方法中執行。 返回結果被傳遞到 onPostExecute(), 在執行任務時呼叫publishProgress() 把執行進度傳遞給 onProgressUpdate()
-
protected void onProgressUpdate(Progress ... progresses) {}
- 執行在UI執行緒, 更新進度資訊, 呼叫 publishProgress() 時被回撥
-
protected void onPostExecute(Result result) {}
- 執行在UI執行緒, 一般在執行完後臺任務後更新UI的操作, 顯示結果等
-
protected void onCancelled() {}
- 執行在UI執行緒, 呼叫 cancel() 被呼叫
原始碼流程
具體用法就不再這裡展示了,接下來我們直接看原始碼,首先看 new AsyncTask() 時候構造方法裡面做了什麼:
構造方法
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
...省略
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...省庫
}
};
}
複製程式碼
2 行:
mWorker
是WorkerRunnable
型別的內部類物件:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
複製程式碼
分析繼承關係,最終 WorkerRunnable
實現了 Callable
介面,介面中只有一個 call()
方法返回值型別是泛型。因為不論是繼承 Thread 類還是實現 Runnable 方法,執行完任務以後都無法直接返回結果。而Callable
介面彌補了這個缺陷,當call()
方法執行完畢以後會返回一個泛型物件。mWorker
重寫了 call
方法,具體實現先不看,等呼叫的時候再具體分析
接著第 8 行:
FutureTask
,它實現了RunnableFuture
介面,RunnableFuture
最終繼承Runnable
和Future
介面
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
複製程式碼
Runnable
介面我們熟悉, 說明 FutureTask
是可以被 Thread 執行的, Future<V>
介面主要是針對Runnable 和 Callable 任務的。提供了三種功能:
- 判斷任務是否完成 ;
- 中斷任務 ;
- 獲取任務執行的結果
**mAsyncTask().execute(); **
接著我們從執行非同步任務的起點開始,進入 execute 方法:
##mAsyncTask
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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 = params;
exec.execute(mFuture);
return this;
}
複製程式碼
5行:判斷任務在執行當中或者已經執行完成, 則報出異常,即每個任務只能執行一次
17行:執行了 onPreExecute() , 當前還在主執行緒
18行:用 mWorker.mParams 儲存傳入的引數
19行:呼叫引數 Executor 型別的 exec 的 execute() 方法, 並把 mFuture 傳進去,exec 是在 execute() 方法中傳的 sDefaultExecutor。
sDefaultExecutor
是 SerialExecutor 類的例項, SerialExecutor 內部維護了一個 ArrayDeque
型別的佇列 mTasks
##mAsyncTask
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = 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() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
複製程式碼
7-12 行:封裝傳入進來的
mTask
新增到mTasks
隊尾,可以看到在run()
方法中,執行mTask
的run()
方法後一定會執行scheduleNext()
16-17 行:判斷 Runnable 型別的mActive
是否為空,第一次執行肯定為空,則呼叫scheduleNext()
21-22 行:取出隊首的元素賦給mActive
,不為空則讓執行緒池去執行這個任務
- 從上面可以分析出來,當佇列中有任務時,任務的執行順序是當佇列中前一個任務執行完成後,後面如果有任務則繼續取出並讓
THREAD_POOL_EXECUTOR
執行緒池去執行,執行完成後繼續佇列中的後一個任務,知道佇列中沒有元素為止。而重複執行AsynscTask.execute()
並不會多次觸發執行緒池,只有佇列中沒有任務時,才會觸發執行緒池執行新增進來的任務。所以,安卓7.0下的AsyncTask
任務執行是序列的,相當於單執行緒池的效果。
我們繼續看任務的執行,即執行緒池去執行 mTask
的 run()
方法:
##FutureTask
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> 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);
}
}
複製程式碼
6 行:
callable
就是 FutureTask 構造方法中傳入進來的mWorker
,然後賦值給c
11 行:執行 mWorker 的call()
方法,並得到執行結果result
, 而 mWorkder 在 AynscTask 構造方法中重寫了 call() 方法
##AsyncTask
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {...}
}
複製程式碼
4 行:標記位
mTaskInvoked
設定為 true
7 行:設定執行緒優先順序 為10。優先順序分為 1-10,數值越大優先順序越高,預設是5。
9 行:出現了doInBackground()
, 此時方法呼叫在 mTask 的 run() 中,即在非同步執行緒中執行 doInBackground() 並得到返回結果 result
15 行:postResult(result)
,字面意思是把結果 post 出去。方法實現先不看,因為後面也呼叫了此方法再具體分析
繼續看 FutureTask run() 方法的 19 行:set(result)
##FutureTask
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL);
finishCompletion();
}
}
複製程式碼
3 行:用
outcome
儲存執行結果
5 行: finishCompletion():
##FutureTask
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q,
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
複製程式碼
直接看倒數第二行
done()
方法,是不是很眼熟?還記得 AsyncTask 構造方法中 mTask 重寫了 done() 方法嗎
##AsyncTask
public AsyncTask() {
mWorker = new WorkerRunnable<Params, 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);
}
}
};
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
複製程式碼
在 mTask 重寫的 done()
方法中, 把 get() 獲取的任務執行結果傳遞給 postResultIfNotInvoked()
方法,在方法中:如果 mTaskInvoked 不為true,則執行 postResult
; 但是在 mWorker 初始化時就已經 將 mTaskInvoked 為 true,所以一般這個 postResult 執行不到。所以這個 postResult 一般是在 mWorker 執行結束 call() 時執行
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
複製程式碼
可以看到 postResult 中出現了我們熟悉的 Handler 訊息機制,傳遞了一個訊息 message, message.what = MESSAGE_POST_RESULT;message.object = new AsyncTaskResult(this,result);
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
複製程式碼
AsyncTaskResult 就是對 AsyncTask 和 result 資料的簡單封裝。
很容易猜測出來 sHandler 肯定重寫了 handleMessage() 方法去接收這個訊息並處理:
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;
}
}
}
複製程式碼
3 行:說明是主執行緒的 Handler
9 行:msg.what 分兩種 MESSAGE_POST_RESULT 和 MESSAGE_POST_PROGRESS,即結果 result 和 進度progress,這裡先看 result 的處理
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製程式碼
如果任務被 cancel() 則呼叫
onCancelled()
,正常執行完呼叫onPostExecute()
,此時在 Handler 的handleMessage
中,因此是主執行緒執行,最後將狀態置為 FINISHED
在日常開發中一個常用操作就是在 doInBackgroud 中呼叫 publishProgress
把執行進度更新給 onProgressUpdate
,然後在 onProgressUpdate 根據執行進度來做一些 UI 處理,比如顯示進度條之類的:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
複製程式碼
還是通過 sHandler 傳送一個 message , what 是 MESSAGE_POST_PROGRESS
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;
}
}
複製程式碼
在 sHandler 的 handleMessage 中,回撥了
onProgressUpdate
,明顯執行在主執行緒
到這裡,整個 AsyncTask 的執行流程就結束了,看起來好像方法東跳來西跳去有點暈,其實自己對著原始碼多走幾遍就很清晰了 下面有個比較清晰的流程圖,借用自 www.jianshu.com/p/e60c3bb03…
cancel()
此外,在 AsyncTask 中有一個小坑要注意一下,對於 cancel 方法的呼叫:
* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete.
*/
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true); // 標誌位 mCancelled 設定為 true
return mFuture.cancel(mayInterruptIfRunning);
}
複製程式碼
從官方註釋來看,mayInterruptIfRunning
如果為 true 當前非同步任務就會被終止,否則該任務會執行完成。而實際開發中,呼叫 cancel 並不能中斷當前執行的任務。
通過原始碼可以看到,方法內部只是給 AsyncTask 設定一個 "
canceled
" 狀態,然後向執行中的執行緒發出interrupt()
呼叫。在這種情況下,你的執行緒是不可中斷的,也就不會終止該執行緒。所以,這就需要你主動去檢查 AsyncTask 是否已經取消,之後決定是否終止你的操作。因此需要這樣操作
public void onProgressUpdate(Integer... value) {
...
if(isCancelled())
return;
...
}
@Override
protected Integer doInBackground(Void... mgs) {
...
if(isCancelled())
return null;
...
}
...`
複製程式碼
另外在需要結束 AsyncTask 的地方:
if(task != null && !task.isCancelled() && task.getStatus() == AsyncTask.Status.RUNNING) {
task.cancel(true);
task = null;
}
複製程式碼