AsyncTask使用方式:
/**
* @Author: david.lvfujiang
* @Date: 2019/10/15
* @Describe:
*/
public class DownloadTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.e("異步準備", "啓動");
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean == true){
Log.e("異步結束", "結束");
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.e("更新ui", String.valueOf(values));
}
@Override
protected Boolean doInBackground(String... strings) {
Log.e("異步操作", "操作");
for (int i = 0; i < 10; i++) {
if (isCancelled()) {
Log.e("cancell", isCancelled()+"");
break;
}
else {
//通知ui更新
publishProgress(new Integer(i));
Log.e("cancell", isCancelled()+"");
}
}
return true;
}
}
DownloadTask task= new DownloadTask();
task.execute("name");
1.
onPreExecute()
:在這個方法是異步開始之前執行的,可以做一些初始化工作。它所在線程與異步任務的execute方法所在的線程一致,這裏若需要更新UI等操作,則execute方法不能再子線程中執行。
2.doInBackground(String name)
:這個方法是運行在子線程,我們可以進行我們的耗時任務,我們可以每隔一秒在這個方法裏調用一次publishProgress(Integer i)
,之後系統將執行onProgressUpdate(Integer i)
3.onProgressUpdate(Integer... values)
:這個方法運行在主線程,每次調用publishProgress()
後它也會被調用,所以我們可以在這裏面進行UI更新
4.onPostExecute(Boolean aBoolean)
:doInBackground()
方法執行完後這個方法將會執行,我們可以在這做一些收尾工作
5.當如果我們異步任務想要停止的話可以調用cancell( Boolean b)
方法,但是cancel()
僅僅是給AsyncTask
對象設置了一個標識位,發生的事情只有:AsyncTask
對象的標識位變了,doInBackground()
和onProgressUpdate()
還是會繼續執行直到doInBackground()
結束,而onPostExecute()
不會被回調。所以我們需要在doInBackground()
的每次循環中調用isCancelled()
去判斷是否繼續執行任務。
需要注意的是:
1.一個AsyncTask對象不能多次調用execute方法,否則會報異常
看完上面AsyncTask的用法我們會有幾個疑問:
1.爲什麼多次調用一個AsyncTask對象的execute方法會報異常?
2.AsyncTask的生命週期它的每一個方法是怎麼調用的?
3.爲什麼調用cancell()後onPostExecute()不會執行了?
我們從源碼來一步步分析:
我們使用AsyncTask的時候是創建對象然後調用execute方法,那我們先來看構造函數:
297 public AsyncTask() {
//創建WorkerRunnable
298 mWorker = new WorkerRunnable<Params, Result>() {
299 public Result call() throws Exception {
300 mTaskInvoked.set(true);
301 Result result = null;
302 try {
303 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
304 //doInBackground()在這被調用
305 result = doInBackground(mParams);
306 Binder.flushPendingCommands();
307 } catch (Throwable tr) {
308 mCancelled.set(true);
309 throw tr;
310 } finally {
311 postResult(result);
312 }
313 return result;
314 }
315 };
316
//創建FutureTask對象
317 mFuture = new FutureTask<Result>(mWorker) {
318 @Override
319 protected void done() {
320 try {
321 postResultIfNotInvoked(get());
322 } catch (InterruptedException e) {
323 android.util.Log.w(LOG_TAG, e);
324 } catch (ExecutionException e) {
325 throw new RuntimeException("An error occurred while executing doInBackground()",
326 e.getCause());
327 } catch (CancellationException e) {
328 postResultIfNotInvoked(null);
329 }
330 }
331 };
332 }
333
我們可以看到構造函數中初始化了倆個對象mWorker 、mFuture 。mWorker 是WorkerRunnable的實例,WorkerRunnable是AsyncTask的內部類實現了Callable接口,因此mWorker 其實就是一個線程,它啓動的時候會調用call()方法,call()方法又會調用doInBackground(),因此doInBackground()可以執行我們的耗時操作,至於mWorker 什麼時候被啓動我們後面講解:
692
693 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
694 Params[] mParams;
695 }
696
mWorker 是FutureTask的實例,FutureTask可以像Runnable一樣運行,封裝異步任務,然後提交給Thread或線程池執行,然後獲取任務執行結果。不瞭解的可以看這篇博客:FutureTask用法及解析。在這裏FutureTask封裝了mWorker ,所以當我們的線程執行完成後會回調FutureTask的done()方法。構造函數到這解析完畢。
execute()方法源碼:
565 @MainThread
566 public final AsyncTask<Params, Progress, Result> execute(Params... params) {
567 return executeOnExecutor(sDefaultExecutor, params);
568 }
603 @MainThread
604 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
605 Params... params) {
606 if (mStatus != Status.PENDING) {
//拋出異常
607 switch (mStatus) {
608 case RUNNING:
609 throw new IllegalStateException("Cannot execute task:"
610 + " the task is already running.");
611 case FINISHED:
612 throw new IllegalStateException("Cannot execute task:"
613 + " the task has already been executed "
614 + "(a task can be executed only once)");
615 }
616 }
617
618 mStatus = Status.RUNNING;
619
//調用onPreExecute
620 onPreExecute();
621
622 mWorker.mParams = params;
//將線程任務加入隊列
623 exec.execute(mFuture);
624
625 return this;
626 }
可以看到execute()方法有個註解
@MainThread
,說明這個方法必須在主線程中調用。再來看executeOnExecutor()
方法,從606行開始有個判斷,判斷mStatus
的值。mStatus
是用來標識AsyncTask
是否已經在執行異步的一個狀態,默認是Status.PENDING
,因此第一次調用execute()
方法不會發生異常,最後在618行設置mStatus = Status.RUNNING;
因此當我們第二次調用execute方法時將會拋出異常(上文第一個問題解決了)。然後在620行調用了onPreExecute();
,最後呢623行調用了exec.execute(mFuture)
,exec
是AsyncTask
的內部類SerialExecutor
的實例。
235 private static class SerialExecutor implements Executor {
//創建任務隊列
236 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
237 Runnable mActive;
238 //該方法使用同步鎖
239 public synchronized void execute(final Runnable r) {
//創建一個線程加入任務隊列
240 mTasks.offer(new Runnable() {
//這個線程的工作就是啓動傳進來的Runnable 對象
241 public void run() {
242 try {
243 r.run();
244 } finally {
245 scheduleNext();
246 }
247 }
248 });
//第一次執行這個方法會走這個判斷
249 if (mActive == null) {
250 scheduleNext();
251 }
252 }
253
254 protected synchronized void scheduleNext() {
//獲取任務隊列的頭執行任務
255 if ((mActive = mTasks.poll()) != null) {
256 THREAD_POOL_EXECUTOR.execute(mActive);
257 }
258 }
259 }
// Executor THREAD_POOL_EXECUTOR就是一個線程池
205 public static final Executor THREAD_POOL_EXECUTOR;
206
207 static {
208 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
209 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
210 sPoolWorkQueue, sThreadFactory);
211 threadPoolExecutor.allowCoreThreadTimeOut(true);
212 THREAD_POOL_EXECUTOR = threadPoolExecutor;
213 }
SerialExecutor
內部維護了一個任務隊列ArrayDeque
,ArrayDeque
也是集合中的一種,execute()
方法則是創建一個線程加入到任務隊列,而線程則又封裝了我們傳入的Runnable
對象,即mFuture
對象。第一次執行的時候mActive
爲null,因此250行執行了scheduleNext()
方法,scheduleNext()
方法則是從隊列中取出任務用線程池執行(THREAD_POOL_EXECUTOR
是一個線程池),任務執行後則會執行241行到245行
的代碼,243行執行了r.run()
,即執行mFuture.run()
方法,mFuture.run()
內部又調用了mWorker .call()
方法,call()
方法又會調用doInBackground()
。最後244行的finally
代碼塊中又調用了scheduleNext()
方法,因此我們的異步任務就是這樣一個一個的被調用的。就是靠SerialExecutor
內部維護了一個任務隊列,我們把任務按順序的放到隊列中,然後不斷的從隊列取出任務執行直到隊列爲空。
現在我們知道真正執行異步任務的是靠THREAD_POOL_EXECUTOR
這個線程池,並且這個線程池是靜態的,在多線程下創建不同的AsyncTask對象執行異步操作也都是需要串行執行。android開發:AsyncTask實現併發執行異步任務
任務執行異步任務的時候會回調Callable的call()方法,因此我們回到構造函數看:
```c
297 public AsyncTask() {
//創建WorkerRunnable
298 mWorker = new WorkerRunnable<Params, Result>() {
299 public Result call() throws Exception {
300 mTaskInvoked.set(true);
301 Result result = null;
302 try {
303 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
304 //doInBackground()在這被調用
305 result = doInBackground(mParams);
306 Binder.flushPendingCommands();
307 } catch (Throwable tr) {
308 mCancelled.set(true);
309 throw tr;
310 } finally {
311 postResult(result);
312 }
313 return result;
314 }
315 };
316
//創建FutureTask對象
317 mFuture = new FutureTask<Result>(mWorker) {
318 @Override
319 protected void done() {
320 try {
321 postResultIfNotInvoked(get());
322 } catch (InterruptedException e) {
323 android.util.Log.w(LOG_TAG, e);
324 } catch (ExecutionException e) {
325 throw new RuntimeException("An error occurred while executing doInBackground()",
326 e.getCause());
327 } catch (CancellationException e) {
328 postResultIfNotInvoked(null);
329 }
330 }
331 };
332 }
333
先是調用
doInBackground()
執行異步操作,311行調用了postResult(result)
341 private Result postResult(Result result) {
342 @SuppressWarnings("unchecked")
343 Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
344 new AsyncTaskResult<Result>(this, result));
345 message.sendToTarget();
346 return result;
347 }
279 //獲取handler對象
280 private static Handler getHandler() {
281 synchronized (AsyncTask.class) {
282 if (sHandler == null) {
//InternalHandler是一個內部類
283 sHandler = new InternalHandler();
284 }
285 return sHandler;
286 }
287 }
288
postResult(result)
則是使用Handler發送MESSAGE_POST_RESULT
消息。這個Handler又是從哪來的?
671
672 private static class InternalHandler extends Handler {
673 public InternalHandler() {
674 super(Looper.getMainLooper());
675 }
676
677 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
678 @Override
679 public void handleMessage(Message msg) {
680 AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
681 switch (msg.what) {
682 case MESSAGE_POST_RESULT:
683 // 結束異步
684 result.mTask.finish(result.mData[0]);
685 break;
//更新UI
686 case MESSAGE_POST_PROGRESS:
687 result.mTask.onProgressUpdate(result.mData);
688 break;
689 }
690 }
691 }
663 private void finish(Result result) {
664 if (isCancelled()) {
665 onCancelled(result);
666 } else {
667 onPostExecute(result);
668 }
669 mStatus = Status.FINISHED;
670 }
InternalHandler
是AsyncTask
內部的一個靜態內部類。我們看到它的構造函數則是獲取主線程的Looper
對象和handler
進行綁定。所以我們使用這個handler發送消息時就可以在主線程中更新UI。可以看到handleMessage()
調用了finish()
和onProgressUpdate()
因此
doInBackground()
執行完成後最後會調用到finish()
,onPostExecute()
則會被執行。同時你可以看到如果我們調用了cancell()
,,onPostExecute()
則不會被執行,而是執行了onCancelled(result)
(解決了上文我們的第三個問題)
我們通知更新UI的時候是在
doInBackground()
調用publishProgress
(),它就是使用handler發送一個MESSAGE_POST_PROGRESS
消息,handler收到後則執行onProgressUpdate()
655 @WorkerThread
656 protected final void publishProgress(Progress... values) {
657 if (!isCancelled()) {
658 getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
659 new AsyncTaskResult<Progress>(this, values)).sendToTarget();
660 }
661 }
總結:
其實整個異步任務的大概流程就是這樣子的,其中涉及的知識點比較多,這裏總結一下:
1.AsyncTask內部維護了一個任務隊列,它會把我們的異步任務封裝成一個Callable實例,然後添加到任務隊列中,並且使用線程池不斷的從任務隊列中取出任務執行,而Callable實例最後會回調doInBackground()。
2.onPreExecute()方法主要用於在異步任務執行之前做一些操作,execute方法不能再子線程中執行。
3.通過剛剛的源碼分析可以知道異步任務一般是順序執行的,即一個任務執行完成之後纔會執行下一個任務。
doInBackground這個方法所在的線程爲任務所執行的線程,在這裏可以進行一些後臺操作。
4.AsyncTask有個handler內部類,構造方法綁定主線程的looper對象,異步任務執行完成之後會通過handler發送消息,最終回調我們的onPostExecute()方法,在doInBackground()方法中通知更新ui調用publishProgress()方法,publishProgress也是通過handler發送消息,最後回調onProgressUpdate()。
5.異步任務對象不能執行多次,即不能創建一個對象執行多次execute方法。(通過execute方法的源碼可以得知)