AsyncTask is designed to be a helper class around
Thread
andHandler
and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.)
AsyncTask
是用來幫助我們更好的使用 Thread
和 Handler
,最好是用於處理短時間異步任務。
使用 AsyncTask 解決異步問題
// 實現AsyncTask的子類
class MyAsyncTask(val name: String) : AsyncTask<Params, Progress, Result>() {
// 執行任務之前的準備工作,比如將進度條設置爲Visible,工作在主線程
override fun onPreExecute() {
Log.d("async", "onPreExecute")
}
// 在onPreExecute()執行完之後立即在後臺線程中調用
override fun doInBackground(vararg params: String?): Any? {
Log.d("async", "$name execute")
Thread.sleep(1000)
publishProgress(1)
return null
}
// 調用了publishProgress()之後,會在主線程中被調用,用於更新整體進度
override fun onProgressUpdate(vararg values: Int?) {
Log.d("async", "progress is: $values")
}
// 後臺線程執行結束後,會把結果回調到這個方法中,並在主線程中被調用
override fun onPostExecute(result: Any?) {
Log.d("async", "onPostExecute")
}
}
// 調用
MyAsyncTask("one").execute("")
MyAsyncTask("two").execute("")
MyAsyncTask("three").execute("")
MyAsyncTask("four").execute("")
// 結果
19:29:01.472 1786-1805/com.taonce D/async: one execute
19:29:02.514 1786-1832/com.taonce D/async: two execute
19:29:03.868 1786-1833/com.taonce D/async: three execute
19:29:04.871 1786-1833/com.taonce D/async: four execute
AsyncTask
必須被子類實例化才能使用,其中必須要實現的方法是:doInBackground(vararg params: String?): Any?
, 其餘三個方法是可選取實現的,具體的解釋在上面的代碼中都解釋過了。還剩下三個泛型參數,下面一一來解釋下:
-
Params
:執行任務所需要的信息; -
Progress
:執行任務的時候,對外發布的進度; -
Result
:任務執行完成後的結果。
到這爲止,你是否發現了一個現象,在結果打印的信息中,每個日誌都是相隔1s,仔細看看是不是,暫且留下一個坑放着,後面會分析給大家聽。下面開始進入源碼分析:
構造函數
先簡單的分析下構造函數中的代碼:
public AsyncTask(@Nullable Looper callbackLooper) {
// 初始化mHandler,如果callbackLooper爲空或者爲sMainLooper,那就使用getMainHandler()的Handler
// 否則使用傳進來的callbackLooper
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// 初始化一個WorkerRunnable對象,它是Callable的實現類,它的call()方法可以擁有返回值
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
...
}
};
// 初始化FutureTask對象,它傳入了mWorker對象
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
}
};
}
mWorker
和 mFuture
暫時不分析,等到調用的時候我們再結合源碼看,目前我們只需要知道初始化了這些對象就夠了。
execute()
接着我們來看看執行一個任務的入口 execute()
:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
// 調用executeOnExecutor(executor,params),傳入的是sDefaultExecutor
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
// 這裏解釋了爲什麼不能重複調用execute()方法
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)");
}
}
// 將狀態標記爲Running
mStatus = Status.RUNNING;
// 調用onPreExecute(),它是最先調用的
onPreExecute();
// 將params傳給mWorker
mWorker.mParams = params;
// 執行sDefaultExecutor.execute(runnable)方法
exec.execute(mFuture);
// 返回自身
return this;
}
我們調用 AsyncTask.execute(parasm)
方法其實就是調用了 sDefaultExecutor.execute(mFuture)
方法,我們順藤摸瓜下去。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
// 雙端隊列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
// 添加到隊列中,並且執行runnable.run()
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
// 執行完r.run()之後依舊會調用一次scheduleNext()方法
// 也就是循環的取隊列中的Runnable,直到Runnable爲空
// 從這裏就可以看出,任務都是按照串行的形式來執行的,必須等待一個任務執行完畢纔去取隊列中的下一個,也就解釋了開頭的每個日誌打印相隔1s的原因。
scheduleNext();
}
}
});
// mActive第一次必然爲空
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
// 從隊列中取出Runnable,並且讓線程池去執行
// THREAD_POOL_EXECUTOR是一個線程池,配置在源碼的最上方
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExecutor
就是一個實現了 Executor
的靜態內部類,它的 execute(runnable)
接收 Runnable
對象,然後在 executeOnExecutor(Executor exec, Params... params)
方法中傳入的是 mFuture
對象,所以在執行到 scheduleNext()
的時候,就會調用 mFuture
的 run()
方法。
FutureTask.run()
:
public void run() {
...
try {
// 拿到Callable對象
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 執行Callable.call()方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// 將Callable.call()方法set()起來,可通過get()獲取
set(result);
}
}...
}
FutureTask.run()
方法就是調用它的 Callable
變量的 call()
方法,在 AsyncTask
中,傳入的 Callable
就是 mWorker
對象,這時候我們就可以把構造函數中的 mWorker
初始化的代碼搬出來讀一讀了。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 獲取doInBackground(params)的返回值
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
// 出現異常將mCancelled設置爲true
mCancelled.set(true);
throw tr;
} finally {
// 回調結果
postResult(result);
}
return result;
}
};
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mWorker
其實很簡單,就是拿到 doInBackground(params)
的返回值,無論是否出現異常,都將結果回調到主線程,回調的操作看下面的 postResult(result)
private Result postResult(Result result) {
// getHandler()就是獲取mHandler對象,默認綁定的是主線程Looper
// 而mHandler在構造函數中已經解釋過,拿到的就是sHandler
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
通過 getHandler().obtainMessage()
獲取一個 Message
對象,將消息的 what
設置爲 MESSAGE_POST_RESULT
標記爲結果不是進度,obj
設置爲 AsyncTaskResult
對象,它是一個內部類,包含了 AsyncTask
和 Data[]
兩個參數。通過 sHandler
將消息發送出去,然後我們看看它是怎麼將 onPostExecute(result)
聯繫起來的。
sHandler
是 InternalHandler
的實例,我們從 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;
}
}
}
InternalHandler
判斷了消息的類型,一種是結果,一種是進度,如果是結果的話,那麼就調用 AsyncTask
的 finish()
方法;如果是進度的話,那麼就調用 onProgressUpdate()
方法,這個方法在前面就已經介紹過了,就是更新整體進度的方法,我們只要看下 finish()
方法就ok了:
private void finish(Result result) {
// 任務是否被取消
if (isCancelled()) {
// 任務被取消的話,那麼就調用onCancel(),這個方法也是可以重寫的
onCancelled(result);
} else {
// 沒有被取消就去調用onPostExecute()
onPostExecute(result);
}
// 將狀態置爲FINISHED
mStatus = Status.FINISHED;
}
源碼分析到此的話,大致的流程已經完成了,通過 execute()
方法將需要執行的任務添加到 SerialExecutor
的雙端隊列中,然後讓 THREAD_POOL_EXECUTOR
線程池去依次執行隊列中的 FutureTask.run()
,這裏需要注意,執行任務是一個一個執行,執行結束之後再去執行另外一個,這個線程池相當於單線程模式,以串行的形式來執行任務的,而不是並行來執行,整個線程池中真正工作的只有那一個核心線程。通過執行 FutureTask.run()
方法去間接執行 mWorker.call()
方法,在 call()
方法中獲取 doInBackground()
方法返回的結果,最後通過 postResult()
和 InternalHandler
來將結果回調到 onPostExecute()
方法中。
源碼分析的文章還在不斷的更新,如果本文章你發現有不正確或者不足之處,歡迎你在下方留言或者掃描下方的二維碼留言也可!