Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發:AsyncTask源代碼完全解析

Android開發:AsyncTask源代碼完全解析

編輯:關於Android編程

從事Java開發以來,接觸過很多的開源代碼,自己能夠明白代碼但是想要表達出來卻有點困難,從今天開始,逐漸開始對一些開源代碼進行解析並記錄成blog分享出來,希望以此提升自己的表達能力,如果文章中有什麼出錯之處,歡迎讀者在評論中指出方便我及時的修正,以免誤導其他讀者,如果你有什麼更好的建議也歡迎在下方留下你的評論,本人不勝榮幸。轉載此文的朋友請帶上好,扯淡的話到此為止,接下來進入正題。
作為一名Android開發者,一定對AsyncTask這個異步操作類不會陌生。這是我從大二開始接觸安卓開發以來,最具爭議性的一個類了,我看到網上很多人都在吐槽AsyncTask的性能多差,並發性多不行等等,在我所閱讀過的開源框架的代碼中很多異步操作也基本不用AsyncTask,都是采用Java的線程池操作,所以以至於有一段時間我也是從來不使用AsyncTask,認為AsyncTask的弊端很多,後來在公司裡的一位大神說了一句“你沒發現,AsyncTask已經封裝好了異步線程和UI線程之間的交互了嗎?Google提供這個類出來給開發者使用自然有他的道理”,我仔細一想,大神說的也挺有道理,回想之前看到的框架基本采用線程池來做異步操作,基本都是因為這些框架裡面都沒有涉及到與UI之間的交互,所以完全可以直接使用線程池來完成。於是便花一些時間好好琢磨一下AsyncTask的源代碼(其實這代碼已經開過好多遍了,每次看總是有新的體會),又瞎扯了一段內容。
日常開發中使用AsyncTask方式就像官方文檔提供出來的這段Demo Code一樣,首先繼承AsyncTask類實現相應的泛型參數,並重寫實現doInBackground抽象方法,這個方法就是用來執行我們需要的異步操作的。
這裡寫圖片描述
如何使用AsyncTask來做異步操作呢,很簡單,同樣來一段官方的Demo Code
這裡寫圖片描述
只需要new出這個AsyncTask並調用execute參入相應參數,即可以完成相應的異步操作流程,並最後將結果返回onPostExecute所處的主線程中進行處理,調用起來確實非常的方便就實現了和兩個線程之間的交互。到此,我們已經重溫了一邊AsyncTask的基本使用,如果身為Android程序員你還不熟悉的話,想必應該去廁所面壁思過了。最後漏了補充一下,在使用AsyncTask編程時候需要注意的一些地方<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqO6PC9wPgpBc3luY1Rhc2u21M/zsdjQ69TaVUnP37PMtLS9qCBleGVjdXRlt723qLHY0OvU2lVJz9+zzLX308MgsrvSqtTaxOO1xLPM0PLW0Mil1rG907X308NvblByZUV4ZWN1dGUoKSwgb25Qb3N0RXhlY3V0ZSwgZG9JbkJhY2tncm91bmQsPGJyIC8+Cm9uUHJvZ3Jlc3NVcGRhdGW3vbeoINK7uPZBc3luY1Rhc2u21M/z1rvE3Na00NDSu7TOo6y8tNa7xNy199PD0ru0zmV4ZWN1dGW3vbeoo6y38dTyu+GxqNTL0NDKsdLss6MgQXN5bmNUYXNrsrvKx7G7yei8xs6qtKbA7brEyrGy2df3tcSjrLrEyrHJz8/ezqq8uMPr1tOjrMjnufvSqtf2s6S6xMqxstnX96Osx7/B0r2o0unE48q508NFeGVjdXRvcqOsVGhyZWFkUG9vbEV4ZWN1dG9y0tS8sEZ1dHVyZVRhc2sKPHA+vdPPwsC0v6rKvM6nudtBc3luY1Rhc2vUtLT6wuujrMrXz8jOp7nb0rvPwrm51Oy6r8r9us3Su9Cpz+C52LXEseTBvzxiciAvPgo8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20150214/201502140831128.png" title="\" />
CPU_COUNT:CPU的核數
CORE_POOL_SIZE :最線程池並發線程數,直接和CPU核數掛鉤
MAXIMUM_POOL_SIZE:線程池中的最大線程數,直接和CPU核數掛鉤
KEEP_ALIVE:線程池中線程的最大存活時間
sThreadFactory:用於構造線程池的工廠,重寫了newThread方法為每個線程做了命名,方便調試跟蹤
sPoolWorkQueue:線程池中的工作隊列,最大容量128的鏈式阻塞隊列
THREAD_POOL_EXECUTOR:多任務並發工作線程池,初始化參數為以上的各大參數
SERIAL_EXECUTOR:串行工作線程池,實現類為下面的SerialExecutor
這裡寫圖片描述
sHandler:AsyncTask類的中用於線程交互的Handler,實現類為內部類InternalHandler(AsyncTask的實現就是將Thread和Handler進行封裝來達到交互,省去我們變成時候自己封裝的過程)
MESSAGE_POST_RESULT:sHandler返回結果的消息類型
MESSAGE_POST_PROGRESS:sHandler返回進度的消息類型
sDefaultExecutor:AsyncTask類的默認線程池,默認使用SERIAL_EXECUTOR串行線程池進行初始化
mWorker:實現Callable接口的類,在構造函數中初始化
mFuture:FutureTask實例,和mWorker搭配使用,同樣在下面的構造函數中初始化
mStatus:標記當前AsyncTask的運行狀態,有PENDING, RUNNING, FINISHED三種分別用於表示等待運行,運行中和運行結束這三種狀態
mCancelled:運行任務被取消的標記位
mTaskInvoked:任務已經被調度運行的標記位
這裡寫圖片描述
到此,我們已經粗略的窺探完一些基礎部分的代碼,先了解上面這些才能夠方便理解接下來繼續圍觀的代碼流程,我們知道,要執行一個AsyncTask,只需要在new出來後,調用execute方法就開始了一個異步任務,接下來就從execute的代碼開始下手,官方文檔上顯示,提供的execute方法有兩個,我們平常經常調用的是下面紅色框框中的那個
這裡寫圖片描述
接下來就跟進這裡的代碼,接著進行圍觀
這裡寫圖片描述
大家可以看到execute函數中指調用了executeOnExecutor函數,並將其結果返回而已,而executeOnExecutor究竟用來干啥,從名字上看,顧名思義應該是在線程池中執行某個線程任務,他傳入的參數有兩個,除了execute本身傳入的變長參數以外,就是AsyncTask類的靜態變量sDefaultExecutor,這個靜態的線程池我已經在前面介紹過了,默認是一個單任務串行的線程池,既然調用了executeOnExecutor那接下就圍觀executeOnExecutor裡面的代碼
這裡寫圖片描述
從這個函數的實現上看,進入這個函數後,首先就開始判斷當前執行的AsyncTask的狀態,如果是PENDDING,說明當前的AsyncTask從未執行過,則直接把mStatus狀態標記為RUNNING狀態,如果AsyncTask已經執行過execute方法,則此處的mStatus將不會再是PENDDING狀態將直接拋出異常,這就可以很好的解釋清楚為什麼一個AsyncTask不能調用兩次execute
這裡寫圖片描述
因為第一次調用的時候已經將PENDDING狀態置為其他狀態,再次判斷到位非PENDDING狀態調用就直接拋IllegalStateException了。
將mStatus狀態進行設置後,接下來就會調用onPreExecute方法,
這裡寫圖片描述
這個方法實際上就是一個空方法,由於調用次方的時依舊處於UI線程中,所以AsyncTask的子類可以重寫此方法並在裡面實現一些UI操作,這也是AsyncTask設計的時候留下來的一個擴展方法,可以用來做異步操作開始前的一些預備工作。
這裡寫圖片描述
緊跟著,就開始將從execute傳入的變長參數,存放到mWorker的成員變量中,並使用默認串行線程池來進行並發操作
這裡寫圖片描述
到此,executeOnExecutor函數的講解基本完成,接下來繼續跟入到exec的execute代碼中,又需要重新的回到了SerialExecutor中的代碼和AsyncTask的構造函數中去,在此借機浏覽mWorker類的實現代碼
這裡寫圖片描述
可以看到,WorkerRunnable實際上是一個帶有mParams成員變量的一個抽象靜態內部類而且繼承了Callable,mWorker真是此類的實現實例,在executeOnExecutor中可以見到,mWorker中的成員變量mParams正是用來存放我們在execute調用的時候傳入的參數。
這裡寫圖片描述
在構造函數中可以看到mWorker被實例化並實現了Callable接口中的call函數(這個函數就是和線程池中執行Runnable接口中的run函數一樣,只不過Callable可以返回結果狀態),進入執行call函數中的代碼,首先現將標記當前任務被調用執行的標記位設置為true,接下來設置好線程的執行優先級,就開始就開始進入我們一直最常見的doInBackground函數裡面了,並將參數傳入,怎樣,有木有一步一步的更加明白調用的過程,在doInBackground被執行完了之後就會將執行結果返回作為調用postResult函數的參數,在postResult函數中你會見到我們更見熟悉的Handler,由它將doInBackground中的執行結果返回給我們的UI線程的消息隊列中,最終由Handler中handleMessage來完成調用AsyncTask中finish方法,而finish方法中最終會判斷doInBackground是正常執行完了,還是中途被取消掉,由此最終來確定是調用onCancelled還是onPostExecute,最後將AsyncTask的執行狀態設置為FINISH來完成我們整個異步線程和UI線程之間的交互過程。這裡還有另外一個地方需要注意一下,就是mFeture中的done方法,主要是用於處理execute的AsyncTask沒有被執行的情況。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
需要注意的是postResult中發送出去的消息類型是MESSAGE_POST_RESULT,這種消息的類型是表示著doInBackground被執行完的情況,但是你可以看到上面的InternalHandler類中,其實處理的消息有兩種,這兩種消息也在前面介紹成員變量的時候也提到過,另外一種消息的類型主要是用於向UI線程更新異步任務中執行任務的進度用的,例如我們最經常接觸到的更新下載進度的例子,實際就是在doInBackground方法中不斷的去調用下面的publishProgress方法,所以你也可以很清楚的看到,實際上也是在使用Handler發送MESSAGE_POST_PROGRESS類型的消息,最後在onProgressUpdate方法中進行相應的更新操作
這裡寫圖片描述
這裡寫圖片描述
而onProgressUpdate方法你也可以看到實際上也是AsyncTask留給我們擴展重寫的方法
這裡寫圖片描述
到此,我們已經基本了解了整個AsyncTask的整個工作的過程,接下來我們再接著看看AsyncTask執行異步的並發性。這回先不急著看代碼,先瞧瞧Google提供的API文檔接口裡面的一些東西。
這裡寫圖片描述
這裡寫圖片描述
AsyncTask提供了兩個類型的線程池,前面已經介紹過串行工作線程池和並發工作線程池,而執行AsyncTask的函數也有兩個,一個是execute,另一個是executeOnExecutor,我們上面的代碼也見到了,實際上我們調用execute在代碼中也是調用了executeOnExecutor函數,那麼跟大家說這些和AsyncTask執行異步任務的並發性有神馬關系呢?我們重新回到SerialExecutor中的代碼裡面去
這裡寫圖片描述
我們所有進行execute的AsyncTask,其本質都是最終被包裝到一個FutureTask裡面,然後放到一個Executor裡面去執行,如果我們不另外指定這個Executor,那麼默認就會用SerialExecutor來執行這些任務,SerialExecutor內部通過一個數組隊列ArrayDeque來組織這些被執行任務,從代碼中可以看到,每執行完一個任務,就會在ArrayDeque中刪除,你只有在執行完了上一個Runnable任務的時候才可以接著執行下一個任務,如果前面的任務一直不執行完,那麼後面任務永遠也不會被執行到,這就很好解釋了前面為什麼說AsyncTask不適合用來做耗時很長的任務,因為一旦有一個任務耗時太長,在默認的AsyncTask執行機制下,後面的任務都將永遠不會得到執行。如果你仔細的觀察,也會看到執行AsyncTask的線程池都是靜態的類變量,而不是成員變量,所以如果出現非常耗時的任務,將是涉及到app中所有的AsyncTask的問題。
Google在一開始設計AsyncTask的時候,主要是由於Android設備的配置當時還比較差,所以不適合太多的任務一起並發執行,考慮此些原因而將AsyncTask做成這種串行執行任務,不過隨著硬件的發展,Google也考慮到AsyncTask所面臨的多任務並發性問題,所以在Android3.0開始也擴展出了另外的THREAD_POOL_EXECUTOR並發線程池,但是從初始化THREAD_POOL_EXECUTOR的參數來看,Google仍然控制著AsyncTask同時執行任務的數目,並且直接跟CPU的核數掛鉤,這樣做也是在一定程度上考慮了性能的問題,Android3.0拓展出了executeOnExecutor接口,甚至可以通過executeOnExecutor接口在AsyncTask外部定義自己的並發線程池。到此我對AsyncTask的源代碼解析已經差不多要進入尾聲,最後想要再講的一些主要就是AsyncTask的執行狀態,以及取消正在執行的AsyncTask,關於這些主要可以看看下面的幾段代碼
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述編程,如果考慮到並發任務數量的問題,也可以使用3.0之後提供的接口來提高並發操作的數量,但是我們仍需要好好的控制任務的最大並發數量,因為數量太大也會造成app的性能問題,這也是為什麼Google提供的多任務線程池並發任務數目要直接和CPU核數掛上鉤的原因,而追溯到AsyncTask的本質,我們從源代碼中也可以一目了然的看出,它其實就是FutureTask和Handler的結合體。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved