Android AsyncTask源碼分析
Android中只能在主線程中進行UI操作,如果是其它子線程,需要借助異步消息處理機制Handler。除此之外,還有個非常方便的AsyncTask類,這個類內(nèi)部封裝了Handler和線程池。本文先簡要介紹AsyncTask的用法,然后分析具體實現(xiàn)。
基本用法
AsyncTask是一個抽象類,我們需要創(chuàng)建子類去繼承它,并且重寫一些方法。AsyncTask接受三個泛型參數(shù):
Params: 指定傳給任務(wù)執(zhí)行時的參數(shù)的類型
Progress: 指定后臺任務(wù)執(zhí)行時將任務(wù)進度返回給UI線程的參數(shù)類型
Result: 指定任務(wù)完成后返回的結(jié)果的類型
除了指定泛型參數(shù),還需要根據(jù)需要重寫一些方法,常用的如下:
onPreExecute(): 這個方法在UI線程調(diào)用,用于在任務(wù)執(zhí)行前做一些初始化操作,如在界面上顯示加載進度控件
doInBackground: 在onPreExecute()結(jié)束之后立刻在后臺線程調(diào)用,用于耗時操作。在這個方法中可調(diào)用publishProgress方法返回任務(wù)的執(zhí)行進度
onProgressUpdate: 在doInBackground調(diào)用publishProgress后被調(diào)用,工作在UI線程
onPostExecute: 后臺任務(wù)結(jié)束后被調(diào)用,工作在UI線程
源碼分析
下面分析這個類的實現(xiàn),主要有線程池以及Handler兩部分。
1、線程池
當(dāng)執(zhí)行一個AsyncTask的時候調(diào)用的是execute()方法,就從這個開始看:
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; //先執(zhí)行 onPreExecute onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; }
execute方法會調(diào)用executeOnExecutor。在這個方法中先檢查任務(wù)是否已經(jīng)執(zhí)行或者執(zhí)行結(jié)束,然后把任務(wù)標記為running。最開始執(zhí)行的是onPreExecute,接著把參數(shù)賦值給mWorker對象。這個mWorker是一個Callable對象,最終被包裝為FutureTask,代碼如下:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; 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 occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
從上面的代碼可以看出,在mWorker對象中的call()方法會調(diào)用doInbackground,返回值交給postResult方法,這個方法通過Handler發(fā)送消息,這一點稍后再詳細分析。
在mWorker對象被封裝成FutureTask之后交由線程池執(zhí)行,從execute方法可以看出,使用的是sDefaultExecutor,它的值默認為SERIAL_EXECUTOR,也就是串行執(zhí)行器,實現(xiàn)如下:
private static class SerialExecutor implements Executor { //線性雙向隊列,用來存儲所有的AsyncTask任務(wù) final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); //當(dāng)前正在執(zhí)行的AsyncTask任務(wù) Runnable mActive; public synchronized void execute(final Runnable r) { //將新的AsyncTask任務(wù)加入到雙向隊列中 mTasks.offer(new Runnable() { public void run() { try { //執(zhí)行AsyncTask任務(wù) r.run(); } finally { //當(dāng)前任務(wù)執(zhí)行結(jié)束后執(zhí)行下一個任務(wù) scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { //從任務(wù)隊列中取出隊列頭部的任務(wù),如果有就交給并發(fā)線程池去執(zhí)行 if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } } public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
在上面的代碼中,如果有任務(wù)執(zhí)行,那么SerialExecutor的execute方法會被調(diào)用,它的邏輯是把Runnable對象加入ArrayDeque隊列中,然后判斷mActivie是否為空。第一次執(zhí)行時mActive當(dāng)然為空,所以執(zhí)行scheduleNext,其實就是取出任務(wù)隊列中的第一個任務(wù)交給線程池(THREAD_POOL_EXECUTOR)執(zhí)行。加入mTask隊列的Runnable對象的run方法里最終一定會調(diào)用scheduleNext,那么又會從任務(wù)隊列中取出隊頭任務(wù)執(zhí)行。這樣便實現(xiàn)了單線程順序執(zhí)行任務(wù),所以在AsyncTask中默認啟用的是單線程執(zhí)行,只有上一個任務(wù)執(zhí)行后才會執(zhí)行下一個任務(wù)。如果想要啟用多線程執(zhí)行任務(wù),可以直接調(diào)用 executeOnExecutor(Executor exec, Params... params),這里的Executor參數(shù)可以使用AsyncTask自帶的THREAD_POOL_EXECUTOR,也可以自己定義。
2、Handler
AsyncTask內(nèi)部用Handler傳遞消息,它的實現(xiàn)如下:
private static class InternalHandler extends Handler { @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; } } }
如果消息類型是任務(wù)執(zhí)行后的返回值(MESSAGE_POST_RESULT)將調(diào)用finish()方法:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
從上面可以知道,如果任務(wù)取消了,將調(diào)用onCancelled,否則調(diào)用onPostExecute,所以一個AsyncTask任務(wù)如果取消了,那么onPostExecute將不會得到執(zhí)行。
如果消息類型是執(zhí)行進度(MESSAGE_POST_PROGRESS)將調(diào)用onProgressUpdate,這個方法默認是空方法,我們可以根據(jù)自己的需要重寫。
總結(jié)
AsyncTask的主要邏輯就如上面所分析的,總結(jié)幾個需要注意的地方:
1)、 AsyncTask的類必須在UI線程加載(從4.1開始系統(tǒng)會幫我們自動完成)
2)、 AsyncTask對象必須在UI線程創(chuàng)建
3)、 execute方法必須在UI線程調(diào)用
4)、 不要手動調(diào)用onPreExecute()、doInBackground、onProgressUpdate方法
5)、 一個任務(wù)只能被調(diào)用一次(第二次調(diào)用會拋出異常)
其它還有一些細節(jié)可以自行研究源碼,另外推薦幾篇不錯的文章:
Android AsyncTask完全解析,帶你從源碼的角度徹底理解
- Android帶進度條的文件上傳示例(使用AsyncTask異步任務(wù))
- Android中通過AsyncTask類來制作炫酷進度條的實例教程
- Android AsyncTask用法巧用實例代碼
- Android屏幕旋轉(zhuǎn) 處理Activity與AsyncTask的最佳解決方案
- Android AsyncTask實現(xiàn)機制詳細介紹及實例代碼
- Android中使用AsyncTask實現(xiàn)文件下載以及進度更新提示
- 詳解Android App中的AsyncTask異步任務(wù)執(zhí)行方式
- Android AsyncTask完全解析 帶你從源碼的角度徹底理解
- Android中使用AsyncTask做下載進度條實例代碼
相關(guān)文章
Android Studio 3.1.3升級至3.6.1后舊項目的兼容操作方法
這篇文章主要介紹了Android Studio 3.1.3升級至3.6.1后舊項目的兼容操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03實例講解Android app開發(fā)中ListView的基本使用及優(yōu)化
這篇文章主要介紹了Android app開發(fā)中ListView的基本使用及優(yōu)化,ListView視圖組件是Android中最常用的組件之一需要的朋友可以參考下2016-02-02Android開發(fā)手冊TextInputLayout樣式使用示例
這篇文章主要為大家介紹了Android開發(fā)手冊TextInputLayout樣式使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06