前言:最近的目標(biāo)就是跳槽,跳槽完再搭個(gè)站,這樣的話生活就充實(shí)一點(diǎn)點(diǎn)了吧。
今天說的AsyncTask也是項(xiàng)目中常常會用到的,面試中常常會問到的。可是我一般都不用這個(gè)的,但是新項(xiàng)目中也需要網(wǎng)絡(luò)請求框架啊,而且asynctask這種不需要new thread和handler就能實(shí)現(xiàn)異步更新,我是非常贊賞的,因?yàn)楹芏嗑W(wǎng)絡(luò)框架其實(shí)都是基于這樣的消息通知機(jī)制,把網(wǎng)絡(luò)請求封裝在底層,用戶只需要重寫回調(diào)接口的方法即可!
Enum枚舉類
經(jīng)過我長久以來積攢的經(jīng)驗(yàn),當(dāng)分析源碼的時(shí)候,最好先找一下類中的枚舉常量,知道他們大概是代表什么意思,在什么情景下會使用到
在任務(wù)的生命周期,任務(wù)的每一個(gè)的狀態(tài)只會被指定一次,如果任務(wù)還沒有被執(zhí)行,那么狀態(tài)為PENDING,如果任務(wù)正在運(yùn)行,那么指定為RUNNING,如果任務(wù)已經(jīng)執(zhí)行完畢了,那么狀態(tài)指定為FINISHED。一個(gè)整體上的了解,AsyncTask有三個(gè)狀態(tài),分別為運(yùn)行前,中,后。
AsyncTask
AsyncTask能夠在UI線程使用,它不需要操作Thread和Handler就可以實(shí)現(xiàn)進(jìn)行后臺操作和在UI線程顯示結(jié)果。
AsyncTask并不是一個(gè)通用的線程框架,而是作為一個(gè)在Thread和Handler之間的工具類。AsyncTask理論上用來執(zhí)行少量的操作(最多花費(fèi)幾秒)。如果需要保持線程運(yùn)行很長一段時(shí)間,強(qiáng)烈建議你使用java.util.concurrent提供的其他的API,比如Executor,ThreadPoolExecutor和FutureTask(有關(guān)線程池部分的內(nèi)容)。
完成一次異步任務(wù),我們在定義的時(shí)候可以根據(jù)需要定義三種類型的變量Params、Progress、Result,四個(gè)步驟onPreExecute、doInBackground、onProgressUpdate和onPostExecute即可。派生一個(gè)子類,這個(gè)子類必須重寫doInBackground方法,通常還會重寫的第二個(gè)方法是onPostExecute來展示運(yùn)行的結(jié)果。
異步任務(wù)中的三種類型的參數(shù)分別如下:
Params:在任務(wù)執(zhí)行之前,發(fā)送給任務(wù)的參數(shù)類型
Progress:后臺線程計(jì)算的時(shí)候,輸出的進(jìn)度單元
Result:后臺線程計(jì)算的結(jié)果的類型
哲學(xué)參數(shù)都是可選的,為了標(biāo)記一個(gè)類型沒有被使用,簡單的使用Void類型即可,要是什么參數(shù)都不需要設(shè)定的話,那么使用<Void,Void,Void>即可。
AsyncTask創(chuàng)建——執(zhí)行的過程:
AsyncTask的創(chuàng)建
AsyncTask是一個(gè)抽象類,所以要實(shí)例化必須要?jiǎng)?chuàng)建一個(gè)繼承自AsyncTask的子類,我們在創(chuàng)建這個(gè)子類的時(shí)候就會指定各種參數(shù)的類型,在AsyncTask的構(gòu)造函數(shù)中,就做了兩樣事情,一個(gè)是創(chuàng)建一個(gè)Callable對象mWorker,一個(gè)是根據(jù)mWorker創(chuàng)建一個(gè)FutureTask,記住,AsyncTask是一次性的買賣
關(guān)于設(shè)置線程優(yōu)先級,setPriority是JDK提供的方法,而Process.setThreadPriority是Android的方法。我們都知道任何線程他們執(zhí)行的都是Runnable方法,不管是繼承Thread,或者是實(shí)現(xiàn)Runnable接口,實(shí)際上都是實(shí)現(xiàn)的run方法,而Callable我們可以理解成的是一個(gè)有返回值的run方法,這個(gè)Callable到最后也會適配成一個(gè)Runnable,適配的工作就是交由FutureTask來完成。
FutureTask是一個(gè)管理Callable的類,有關(guān)Future相關(guān)的字樣總是離不開Callable的,在AsyncTask的構(gòu)造方法內(nèi),通過mWorker來創(chuàng)建了一個(gè)FutureTask對象
這里先簡單介紹一下FutureTask,F(xiàn)utureTask實(shí)現(xiàn)了RunnableFuture接口,這個(gè)RunnableFuture接口繼承了Runnable和Future接口,F(xiàn)uture接口里面其實(shí)是一些針對Callback的操作,比如cancel,done,get等,用于獲取call的結(jié)果,取消call等。而FutureTask內(nèi)部有一個(gè)Callable<V>的成員變量callable
再看FutureTask的兩個(gè)構(gòu)造函數(shù),可以從這兩個(gè)構(gòu)造函數(shù)中看出,F(xiàn)utureTask的構(gòu)造其實(shí)就是要初始化callable對象,如果傳入的是一個(gè)Runnable對象,就同Executors的callable方法適配成callable對象(Executors其實(shí)是一個(gè)封裝好的工具類)
至此我們就獲得一個(gè)FutureTask,它才是異步任務(wù)的大功臣,AsyncTask只是發(fā)言代表而已。AsyncTask還添加了一些類似于get,cancel等方法,其實(shí)是AsyncTask提供給外部,對FutureTask進(jìn)行直接控制的方法。get方法分阻塞和超時(shí)非阻塞兩種,一般使用情況不多。
AsyncTask的運(yùn)行
當(dāng)一個(gè)異步任務(wù)執(zhí)行的時(shí)候,我們會調(diào)用execute這個(gè)方法,并且任務(wù)的執(zhí)行過程會經(jīng)歷四個(gè)步驟:
以下四個(gè)方法都是需要默認(rèn)空實(shí)現(xiàn)需要我們重寫的方法。
1、onPreExecute(戰(zhàn)前動員)
在任務(wù)執(zhí)行之前,在UI線程中調(diào)用,這一步驟通常用來安裝任務(wù),比如顯示一個(gè)進(jìn)度條在用戶界面,很多時(shí)候我們都沒有重寫這個(gè)方法的。
2、doInBackground
在onPreExecute執(zhí)行完畢之后就會調(diào)用doInBackground,而doInBackground是mWorker中的call回調(diào)中被調(diào)用,這一步被用來執(zhí)行耗時(shí)的后臺計(jì)算,AsyncTask的Params參數(shù)會傳遞到這一步驟,計(jì)算的結(jié)果必須在這一步被返回,當(dāng)我們調(diào)用execute的時(shí)候,就會調(diào)用起這個(gè)方法,執(zhí)行耗時(shí)操作了。
當(dāng)我們調(diào)用AsyncTask的execute時(shí),就會往任務(wù)池里面添加一個(gè)runnable任務(wù),并且自動執(zhí)行任務(wù)池里的任務(wù),執(zhí)行完一個(gè)的時(shí)候執(zhí)行下一個(gè),正常情況下其實(shí)只會有一個(gè)任務(wù),那就時(shí)構(gòu)造函數(shù)內(nèi)實(shí)現(xiàn)的mFuture對象
3、onProgressUpdate
execute的時(shí)候,可以在doInBackground中主動調(diào)用publishProgress來輸出一個(gè)或者多個(gè)進(jìn)度值(Progresser類型值),這些值都可以在在onProgressUpdate這一步直接在UI線程通過進(jìn)度條等方式顯示出來。即使后臺計(jì)算仍在運(yùn)行,這個(gè)方法可以在UI進(jìn)程中顯示任何形式的進(jìn)度。比如,可以通過移動一個(gè)進(jìn)度條或者顯示一些logs在文本域。
4、onPostExecute
在后臺計(jì)算結(jié)束后,就會在UI線程調(diào)用onPostExecute,后臺計(jì)算的結(jié)果會作為Result類型的參數(shù)傳遞到這一步。
在構(gòu)造AsyncTask的時(shí)候,再mWorker中調(diào)用postResult來傳遞任務(wù)執(zhí)行的結(jié)果,這個(gè)結(jié)果是最后的結(jié)果了。
容錯(cuò)操作:在FutureTask中方Callable執(zhí)行完畢的時(shí)候調(diào)用done方法,在done方法里面當(dāng)任務(wù)執(zhí)行出錯(cuò),超時(shí)什么的,任務(wù)還沒執(zhí)行就down了的話,就會調(diào)用起這個(gè)方法,要是postResult成功調(diào)用了,那么這個(gè)方法就不會被調(diào)起。反正就是無論如何都要執(zhí)行postResult,代表這個(gè)任務(wù)已經(jīng)結(jié)束了。
關(guān)于Handler消息機(jī)制,大家可以去查看我之前的Handler消息機(jī)制小酌。這么就不細(xì)說了
5、取消一個(gè)任務(wù)
可以在任意時(shí)間取消一個(gè)任務(wù)通過調(diào)用cancel(boolean state),調(diào)用這個(gè)方法將會導(dǎo)致后續(xù)調(diào)用isCanceled()來判斷當(dāng)前任務(wù)狀態(tài)的時(shí)候返回true。
調(diào)用這個(gè)方法之后,任務(wù)將不會正常結(jié)束,onCanceled(Object)會代替onPostExecute被執(zhí)行,在doInBackground方法返回的時(shí)候。為了確保任務(wù)可以盡可能快地取消,應(yīng)該總是周期性地在doInBackground中檢查isCanceled()的返回值,如果可能的話設(shè)置一個(gè)循環(huán)在里面。
使用規(guī)則
為了使這個(gè)類能正常地使用和運(yùn)行,有幾個(gè)規(guī)則必須要遵守的:
AsyncTask類必須在UI線程中加載,在API 16中這是自動完成的,任務(wù)實(shí)例必須在UI線程創(chuàng)建,并且在UI線程調(diào)用execute方法,不要主動調(diào)用onPreExecute,doInBackground,onProgressUpdate,任務(wù)只能夠被執(zhí)行一次,如果試圖執(zhí)行第二次,那么就會拋出異常。
內(nèi)存可觀測
AsyncTask保證所有回調(diào)調(diào)用在這種方式下都是同步的,所以每一個(gè)操作都是安全的,不會出現(xiàn)多線程同時(shí)訪問的問題,不需要指定為同步:
在構(gòu)造器中或者在onPreExecute中設(shè)置成員變量,并且在doInBackground中引用他們。
在doInBackground中設(shè)置成員變量,并且在onProgressUpdate和onPostExecute中引用他們
執(zhí)行的次序
AsyncTask第一次被介紹的時(shí)候,是被連續(xù)執(zhí)行在一個(gè)單一后臺線程中的。從API4開始,改成使用一個(gè)線程池來允許多任務(wù)同步執(zhí)行。自從API 11開始,任務(wù)被執(zhí)行在一個(gè)單一的線程來避免同步執(zhí)行帶來的普通應(yīng)用錯(cuò)誤,如果你真的需要同步執(zhí)行,可以通過threadPoolExecutor調(diào)用executeOnExecutor(java.util.concurrent.Executor, Object[])來實(shí)現(xiàn)。
線程池部分
私有靜態(tài)常量(外部不可直接訪問)
CPU_COUNT:獲取當(dāng)前可用的cpu的計(jì)數(shù)。
CORE_POOL_SIZE:線程池中的核心線程數(shù)等于CPU總數(shù)加1。
MAXIMUN_POOL_SIZE:線程池中最大線程數(shù)等于CPU總數(shù)的兩倍加1。
KEEP_ALIVE:線程執(zhí)行完畢后是否維持線程。
以上的都是常量部分,用于設(shè)置默認(rèn)的線程池實(shí)現(xiàn)。用于創(chuàng)建一個(gè)ThreadPoolExecutor對象,對于這些靜態(tài)常量,同一個(gè)子類所創(chuàng)建的AsyncTask實(shí)例是共用這些對象的。就好像一開始說的,AsyncTask對象是一次性買賣。上面提到的任務(wù)池的作用就是當(dāng)我們提交多個(gè)AsyncTask實(shí)例的時(shí)候,就會進(jìn)入隊(duì)列等待了。
相對于Handler,AsyncTask反而沒那么復(fù)雜了,當(dāng)然了,要是加上了線程池部分的,就復(fù)雜多了啊,不過線程池也不是一時(shí)半刻可以理完了,畢竟有了Thread才有ThreadPool嘛