須菩提。于意云何。如來有所說法不愧怜。須菩提白佛言。世尊看蚜。如來無所說叫搁。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?-----佛說
一??AsyncTask 使用
第一步: 首先我們會寫一個自己的類繼承自AsyncTask?? ,然后重寫doInBackground方法供炎,在繼承AsyncTask的時候我們需要傳遞三個泛型參數(shù)渴逻,分別是Params, Progress, Result,首先我們詳細(xì)說明一下這三個參數(shù)是什么意思音诫。
Params :這個參數(shù)可以傳遞任意類型惨奕,換言之,也就是只要你在doInBackground方法中執(zhí)行耗時任務(wù)的時候需要用到外部對象來調(diào)用該對象的某個耗時方法或者當(dāng)前異步操作需要依賴某個對象的值竭钝,這個時候Params 參數(shù)就傳遞你要用到對象的所屬類型梨撞,因為doInBackground 可變長類型參數(shù)里面的類型是由Params 所決定的雹洗,所以Params 泛型參數(shù)你傳遞什么類型doInBackground 方法的參數(shù)就是什么類型,以上只說明了doInBackground形參的類型卧波,那實參是怎么傳遞進(jìn)去的呢时肿?Params類型對應(yīng)的實際參數(shù)是在執(zhí)行.execute()方法以后傳遞進(jìn)去的,后面會用一個示例說明港粱,?參數(shù)的傳遞不僅僅只傳遞一個參數(shù)螃成,?因為doInBackground里面的是個可邊長參數(shù),具體類型為Params... params .
Progress:這個參數(shù)也可以傳遞任意類型查坪,什么時候會用到這個參數(shù)呢寸宏?當(dāng)子線程執(zhí)行異步操作的時候,主線程需要實時的知道子線程的執(zhí)行情況偿曙,這個時候就要用到Progress參數(shù)氮凝,典型應(yīng)用場景網(wǎng)絡(luò)文件下載或者文件上傳進(jìn)度顯示,下載或者長傳文件是耗時操作需要在子線程執(zhí)行望忆,刷新頁面UI需要在主線程執(zhí)行罩阵,這個時候在doInBackground方法中調(diào)用publishProgress方法將進(jìn)度值轉(zhuǎn)遞進(jìn)去,然后會回調(diào)onProgressUpdate方法炭臭,在onProgressUpdate進(jìn)行UI操作永脓,為什么在子線程中調(diào)用publishProgress后回調(diào)的onProgressUpdate卻運行在主線程中,后面源碼分析我會詳細(xì)說明鞋仍。publishProgress和onProgressUpdate方法共享一個類型的參數(shù)常摧,換言之就是Progress類型,這個泛型你指定什么如上兩個方法的參數(shù)類型就是什么威创。實參可以傳遞多個因為是可變長參數(shù)類型落午。
Result:?這個參數(shù)也可以傳遞任意類型,但和其他兩個參數(shù)不同的是Result不是可變參數(shù)肚豺,只能返回一個結(jié)果溃斋,使用時機(jī),就是當(dāng)我們doInBackground的方法的耗時任務(wù)執(zhí)行完畢以后吸申,需要拿到子線程的執(zhí)行結(jié)果的時候梗劫,這個時候需要在doInBackground 方法中return 一個Result。這個Result要么成功截碴,要么失敗梳侨,分別由會調(diào)用不同的回調(diào),執(zhí)行成功return? 的Result可以在onPostExecute方法中拿到結(jié)果日丹,執(zhí)行失敗直接調(diào)用onCancelled走哺,失敗了沒有結(jié)果,如果非想要個結(jié)果哲虾,那就重寫帶參數(shù)的onCancelled(Result result)方法丙躏。onPostExecute調(diào)用時機(jī)择示,onCancelled調(diào)用時機(jī)以及為什么在主線程調(diào)用。源碼分析會做詳細(xì)說明晒旅。
第二步:針對Params, Progress, Result這三個參數(shù)我們用一個例子來演示栅盲,例子很簡單,就三個學(xué)生和一個老師敢朱,具體流程是剪菱,老師布置作業(yè)讓學(xué)生去做摩瞎,在學(xué)生做作業(yè)的過程中拴签,老師需要實時的知道當(dāng)前是哪個同學(xué)在做題目以及這位同學(xué)做到了哪個題目,等所有學(xué)生做完作業(yè)以后旗们,反饋老師學(xué)生做題這件事情完成蚓哩。
創(chuàng)建學(xué)生類Student
創(chuàng)建老師類 Teacher
創(chuàng)建任務(wù)執(zhí)行類?WorkAsyncTask
示例運行結(jié)果
使用總結(jié)
通過以上一個簡單的小案例演示了AsyncTask的基本使用流程,以及doInBackground方法執(zhí)行完畢以后對執(zhí)行結(jié)果的返回上渴,本案例中通過調(diào)用isCancelled()方法判斷了在doInBackground方法中所執(zhí)行的任務(wù)有沒有執(zhí)行成功岸梨,執(zhí)行成功返回true調(diào)用onPostExecute方法,執(zhí)行失敗返回false調(diào)用onCancelled()的方法稠氮,以上案例只演示了成功的情況,未成功的情況沒有演示,那什么時候會執(zhí)行未成功呢舶吗?當(dāng)在執(zhí)行doInBackground方法的過程中如果執(zhí)行的耗時任務(wù)拋出了異常鸠姨,這個時候會執(zhí)行onCancelled方法。
二? AsyncTask 源碼分析
1 構(gòu)建AsyncTask實例的時候做了哪些事情奢米?
通過第一小部分對AsyncTask的使用抓韩,我們知道在使用AsyncTask的時候首先要自己定義一個類然后繼承AsyncTask,重寫doInBackground鬓长,那當(dāng)我們自己定義一個類在使用的時候也就是new的時候谒拴,AsyncTask構(gòu)造函數(shù)都做了那些事情呢?看下面源碼
分析:
通過源碼可以看出我們在new?AsyncTask的時候有三個構(gòu)造函數(shù)涉波,第一個無參英上,后兩個有參,但是我們在使用的時候只能使用無參的構(gòu)造啤覆,因為其他兩個構(gòu)造函數(shù)是@hide標(biāo)注的所以我們無法調(diào)用苍日,如果非要調(diào)用我們可以通過反射機(jī)制去調(diào)用(API級別28之前,隱藏的方法仍然可以通過java反射訪問城侧,但是沒那個必要易遣,真沒必要!O佑印)豆茫,new?AsyncTask 以后的執(zhí)行流程:
第一步創(chuàng)建主線程唯一一個mHandler
AsyncTask()==========>this((Looper) null)====AsyncTask(@Nullable Looper callbackLooper),然后通過mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper);這句構(gòu)建一個主線程的mHandler 侨歉,這個主線程的mHandler 在后續(xù)回調(diào)執(zhí)行的onProgressUpdate,onPostExecute揩魂,onCancelled發(fā)揮了重要的作用幽邓。這個mHandler=getMainHandler() ,緊接著我們看getMainHandler() 的源碼
在getMainHandler() 里面創(chuàng)建了一個sHandler, sHandler=new InternalHandler(Looper.getMainLooper());接下來我們繼續(xù)跟蹤代碼看InternalHandler時什么
由上面的代碼可以看出InternalHandler繼承自Handler 并且重寫了handleMessage方法,在handleMessage方法里面處理子UI相關(guān)的操縱火脉。至于handleMessage方法什么時候執(zhí)行調(diào)用下面我會詳細(xì)說明
第二步創(chuàng)建WorkerRunnable 實例
WorkerRunnable 是什么牵舵?WorkerRunnable是一個抽象類但是實現(xiàn)了Callable接口,在異步操作過程中如果我們需要獲取到某個線程的執(zhí)行結(jié)果倦挂,這個時候我們需要使用Callable +?FutureTask來完成畸颅,接下來我們看看Callable的call方法做了什么首先會執(zhí)行mTaskInvoked.set(true);、mTaskInvoked是什么方援?mTaskInvoked是一個AtomicBoolean的實例對象(全局唯一没炒,用final修飾僅此一個),關(guān)于AtomicBoolean由于篇幅原因后期會單獨拉一篇來詳細(xì)說明犯戏,這里不做過多解釋送火,mTaskInvoked.set(true)以后會將AtomicBoolean的?value屬性設(shè)置成1 這個value屬性是volatile的保證了線程的可見性,緊接著 Result result =null; 這個Result就是我們傳遞進(jìn)去作為返回結(jié)果的泛型參數(shù)先匪,關(guān)于這個參數(shù)的說明在AsyncTask 使用第一部分已經(jīng)做了詳細(xì)的說明种吸,接下來又執(zhí)行了Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);這句是做什么的?首先普及一個概念.線程調(diào)度機(jī)制呀非,線程的調(diào)度機(jī)制有兩種坚俗,
1 分時調(diào)度模型:所有的線程輪流獲取CPU的使用權(quán),平均分配每個線程占用的CPU時間姜钳。
2 搶占式調(diào)度模型:優(yōu)先讓可運行池中優(yōu)先級高的線程占用CPU坦冠,優(yōu)先級相同的隨機(jī)選擇一個線程。
Android線程調(diào)用模型
?1?Adroid里面采用搶占式調(diào)度模型哥桥,可以通過android.os.Process.setThreadPriority(int)設(shè)置線程優(yōu)先級辙浑,參數(shù)范圍-20----24,數(shù)值越小優(yōu)先級越高,0為默認(rèn)優(yōu)先級拟糕。
2 線程優(yōu)先級 默認(rèn)情況下判呕,新創(chuàng)建的線程與母線程的優(yōu)先級保持一致。
舉例:比如我當(dāng)前有AB兩個線程送滞,A線程為主線程侠草,B線程為在主線程創(chuàng)建的子線程,因為安卓是搶占式調(diào)度模型犁嗅,為了保證UI能夠及時響應(yīng)边涕,這個時候可以把子線程的優(yōu)先級降低。
通過以上說明,call方法里面的這句Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);就是為了設(shè)置線程的優(yōu)先級功蜓。優(yōu)先級設(shè)置完畢以后緊接著就開始執(zhí)行我們都很熟悉的doInBackground();方法并將doInBackground方法執(zhí)行的結(jié)果用result接收园爷。最后調(diào)用了Binder.flushPendingCommands();這句的作用將當(dāng)前線程中所有待處理的Binder命令刷新到內(nèi)核驅(qū)動程序。 在執(zhí)行可能長時間阻塞的操作之前調(diào)用此方法很有用式撼,以確保釋放了所有待處理的對象引用童社,以防止該過程將對象保留的時間超過所需時間。如果在執(zhí)行doInBackground的過程中出現(xiàn)異常會怎么辦著隆?我們緊接著看看catch里面做了啥扰楼,首先會將mCancelled.set(true);mCancelled也是AtomicBoolean類型的,在前面的第一部分說AsyncTask 使用的過程中在?doInBackground方法中調(diào)用了 ? ?if (isCancelled())return false else return true;來判斷當(dāng)先任務(wù)是否執(zhí)行成功美浦,主要的一句就是根據(jù)mCancelled.get()方法弦赖,get到的是false 還是ture來進(jìn)行判斷當(dāng)前任務(wù)有沒執(zhí)行成功,如果get到的是true則說明任務(wù)執(zhí)行失敗反之則成功抵代,最火在finally代碼塊里面調(diào)用了?postResult(result);方法將doInBackground方法執(zhí)行的結(jié)果傳遞進(jìn)去腾节,?從而更新UI,接下來我們看看postResult()的源碼
可以看到postResult中出現(xiàn)了我們熟悉的異步消息機(jī)制荤牍,傳遞了一個消息message, message.what為MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);? ? ?getHandler=mHandler前面講過庆冕。緊接著我們看看AsyncTaskResult時什么
AsyncTaskResult就是一個簡單的攜帶參數(shù)的對象康吵。這里的泛型Data=我們使用AsyncTask的時候第三個參數(shù)Result 也就是doInBackground方法里面返回的Result。那么當(dāng)postResult方法執(zhí)行以后通過mHandler將消息發(fā)出去以后會做什么事情呢访递?收先看圖11的代碼也就是說當(dāng)postResult方法執(zhí)行以后圖11?handleMessage方法會執(zhí)行晦嵌,然后會執(zhí)行case語句里面的MESSAGE_POST_RESULT,也就是會執(zhí)行這句話result.mTask.finish(result.mData[0]);拷姿,首先result.mTask拿到AsyncTask對象惭载,然后調(diào)用AsyncTask的finish方法并將doInBackground的返回結(jié)果傳遞進(jìn)去,緊接著我們看看在finish方法里面做了什么响巢。
由上面的代碼我們可以看出描滔,當(dāng)finish方法執(zhí)行以后,首先會調(diào)用isCancelled()進(jìn)行任務(wù)執(zhí)行校驗踪古,如果在執(zhí)行doInBackground的過程中拋出了異常含长,會將mCancelled.set(true);這個前面已經(jīng)說過,isCancelled()任務(wù)校驗完畢以后伏穆,如果返回false則說明任務(wù)執(zhí)行成功拘泞,然后回調(diào)onPostExecute方法,這下終于明白了onPostExecute方法為什么運行在主線程枕扫,為什么在doInBackground方法里面的返回結(jié)果會在onPostExecute方法里面拿到了吧陪腌,如過isCancelled()返回true則說明任務(wù)執(zhí)行失敗回調(diào)onCancelled方法。至此整個new?WorkerRunnable 的初始化工作以及執(zhí)行流程分析已接近尾聲,但是我們貌似還遺漏了一個東西诗鸭,那就是當(dāng)我們在doInBackground方法里面手動調(diào)用publishProgress()方法以后商叹,onProgressUpdate方法會回調(diào),并且能實時的和主線程交互只泼,接下來我們分析一下調(diào)用publishProgress后做了那些事情剖笙,請看源碼
通過源碼我們可以看出也是通過mHandler發(fā)送一個消息,然后回調(diào)InternalHandler 的handleMessage方法并且會執(zhí)行case分支里面的MESSAGE_POST_PROGRESS分支请唱,然后回調(diào)AsyncTask的onProgressUpdate方法并將從publishProgress傳遞過來的值傳遞過去弥咪。到此整個WorkerRunnable的call方法執(zhí)行流程分析完畢。
mWoker看完了十绑,應(yīng)該到我們的mFuture了聚至,依然是在構(gòu)造方法中完成mFuture的初始化,將mWorker作為參數(shù)本橙,復(fù)寫了其done方法扳躬。看源碼
可以看到當(dāng)mWorker的call方法執(zhí)行完畢以后甚亭,會回調(diào)FutureTask的done方法贷币,done方法執(zhí)行后、會調(diào)用:postResultIfNotInvoked(get());get()表示獲取mWorker的call的返回值亏狰,即Result.然后看postResultIfNotInvoked方法
如果mTaskInvoked不為true役纹,則執(zhí)行postResult(執(zhí)行postResult和上面分析的在call中調(diào)用的postResult的流程是一模一樣的,因為調(diào)用的是同一個方法暇唾,這里再不啰嗦)促脉;但是在mWorker初始化時就已經(jīng)將mTaskInvoked為true,所以一般這個postResult執(zhí)行不到策州。好了瘸味,到了這里,整個AsyncTask的初始化做的事情已經(jīng)全部分析完畢够挂,不過這里一直是初始化這兩個對象的代碼旁仿,并沒有真正的執(zhí)行。下面我們看真正調(diào)用執(zhí)行的地方下硕。
2 .execute()的時候做了哪些事情丁逝?看源碼
分析 :當(dāng)我們.execute()以后首先會執(zhí)行executeOnExecutor()方法,執(zhí)行的時候需要一個Executor梭姓,也就是sDefaultExecutor霜幼,而sDefaultExecutor = SERIAL_EXECUTOR,那么SERIAL_EXECUTOR是誰誉尖?由代碼可以看出來SERIAL_EXECUTOR就是SerialExecutor罪既,緊接著看SerialExecutor里面是什么,跟隨代碼我們可以看到首先里面有一個ArrayDeque的隊列,然后有個Runnable琢感,緊接我們會發(fā)現(xiàn)首先會offer一個任務(wù)進(jìn)去丢间,然后調(diào)用scheduleNext();開始執(zhí)行任務(wù),因為ArrayDeque 是Java里面的一個雙端隊列的線性實現(xiàn)驹针,由此可以得出AsyncTask只使用于執(zhí)行立即需要啟動并且異步執(zhí)行的生命周期短暫的使用場景烘挫。那么線程池時在哪里被開啟的?緊接著我們看executeOnExecutor()內(nèi)部實現(xiàn)源碼柬甥。
當(dāng)execute()調(diào)用以后緊接著調(diào)用executeOnExecutor()
1當(dāng)?executeOnExecutor()執(zhí)行以后首先?設(shè)置當(dāng)前AsyncTask的狀態(tài)為RUNNING饮六,上面的switch也可以看出,每個異步任務(wù)在完成前只能執(zhí)行一次苛蒲。
2執(zhí)行了onPreExecute()卤橄,當(dāng)前依然在UI線程,所以我們可以在其中做一些準(zhǔn)備工作臂外。
3?將我們傳入的參數(shù)賦值給了mWorker.mParams
4?exec.execute(mFuture) 窟扑,當(dāng)這句被調(diào)用以后,圖24里面的run方法會執(zhí)行漏健,run方法執(zhí)行以后嚎货,r.run()開始執(zhí)行,這里的r.run其實調(diào)用的就是FutureTask里面的run方法漾肮,當(dāng)FutureTask里面的run方法開始執(zhí)行以后厂抖,Callable的call方法就會執(zhí)行,因為FutureTask實現(xiàn)了?RunnableFuture接口克懊,而RunnableFuture接口繼承了Runnable和Future接口,再FutureTask里面的run方法里面調(diào)用了Callable的call方法七蜘,call方法執(zhí)行以后谭溉,我們整個AsyncTask就開始運轉(zhuǎn)起來了。到此整個AsyncTask的分析流程全部結(jié)束橡卤。