Android多線程的四種方式:Handler、AsyncTask、ThreadPoolExector、IntentService
Android多線程的四種方式:Handler、AsyncTask、ThreadPoolExector、IntentService
?異步通信機制,將工作線程中需更新UI的操作信息 傳遞到 UI主線程,從而實現(xiàn) 工作線程對UI的更新處理,最終實現(xiàn)異步消息的處理。Handler不僅僅能將子線程的數(shù)據(jù)傳遞給主線程,它能實現(xiàn)任意兩個線程的數(shù)據(jù)傳遞。
(1)Message Message 可以**程之間傳遞消息。
可以在它的內(nèi)部攜帶少量數(shù)據(jù),用于在不同線程之間進行數(shù)據(jù)交換。除了 what 字段,還可以使用 arg1 和 arg2 來攜帶整型數(shù)據(jù),使用 obj 來攜帶 Object 數(shù)據(jù)。 (2) Handler Handler 作為處理中心,用于發(fā)送(sendMessage 系列方法)與處理消息(handleMessage 方法)。 (3) MessageQueue MessageQueue 用于存放所有通過 Handler 發(fā)送的消息。
這部分消息會一直存放在消息隊列中,直到被處理。每個線程中只會有一個 MessageQueue 對象 (4) Looper Looper 用于管理 MessageQueue 隊列,Looper對象通過loop()方法開啟了一個*循環(huán)——for (;;){},不斷地從looper內(nèi)的MessageQueue中取出Message,并傳遞到 Handler 的 handleMessage() 方法中。每個線程中只會有一個 Looper 對象。
AsyncTask 是一種輕量級的任務(wù)異步類,可以在后臺子線程執(zhí)行任務(wù),且將執(zhí)行進度及執(zhí)行結(jié)果傳遞給 UI 線程。 (1)onPreExecute() 在 UI 線程上工作,在任務(wù)執(zhí)行 doInBackground() 之前調(diào)用。此步驟通常用于設(shè)置任務(wù),例如在用戶界面中顯示進度條。
(2)doInBackground(Params… params) 在子線程中工作,在 onPreExecute() 方法結(jié)束后執(zhí)行,這一步被用于在后臺執(zhí)行長時間的任務(wù),Params 參數(shù)通過 execute(Params) 方法被傳遞到此方法中。任務(wù)執(zhí)行結(jié)束后,將結(jié)果傳遞給 onPostExecute(Result) 方法,同時我們可以通過 publishProgress(Progress) 方法,將執(zhí)行進度發(fā)送給 onProgressUpdate(Progress) 方法。 (3)onProgressUpdate(Progress… values) 在 UI 線程上工作,會在 doInBackground() 中調(diào)用 publishProgress(Progress) 方法后執(zhí)行,此方法用于在后臺計算仍在執(zhí)行時(也就是 doInBackgound() 還在執(zhí)行時)將計算執(zhí)行進度通過 UI 顯示出來。
例如,可以通過動畫進度條或顯示文本字段中的日志,從而方便用戶知道后臺任務(wù)執(zhí)行的進度。 (4)onPostExecute(Result result) 在 UI 線程上工作,在任務(wù)執(zhí)行完畢(即 doInBackground(Result) 執(zhí)行完畢)并將執(zhí)行結(jié)果傳過來的時候工作。 使用規(guī)則: (1)AsyncTask 是個抽象類,所以要創(chuàng)建它的子類實現(xiàn)抽象方法 (1)AsyncTask 類必須是在 UI 線程中被加載,但在Android 4.1(API 16)開始,就能被自動加載完成。 (2)AsyncTask 類的實例對象必須在 UI 線程中被創(chuàng)建。
(3)execute() 方法必須是在 UI 線程中被調(diào)用。 (4)不要手動調(diào)用方法 onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate() (5)任務(wù)只能執(zhí)行一次(如果嘗試第二次執(zhí)行,將拋出異常)。即一個AsyncTask對象只能調(diào)用一次execute()方法。 原理: 其源碼中原理還是 Thread 與 Handler 的實現(xiàn),其包含 兩個線程池,一個 Handler,如下所示: 名稱類型作用 SERIAL_EXECUTOR線程池分發(fā)任務(wù),串行分發(fā),一次只分發(fā)一個任務(wù) THREAD_POOL_EXECUTOR線程池執(zhí)行任務(wù),并行執(zhí)行,執(zhí)行的任務(wù)由 SERIAL_EXECUTOR 分發(fā) InternalHandlerHandler負責子線程與主線程的溝通,通知主線程做 UI 工作 一方面減少了每個并行任務(wù)獨自建立線程的開銷,另一方面可以管理多個并發(fā)線程的公共資源,從而提高了多線程的效率。
所以ThreadPoolExecutor比較適合一組任務(wù)的執(zhí)行。Executors利用工廠模式對ThreadPoolExecutor進行了封裝。 Executors提供了四種創(chuàng)建ExecutorService的方法,他們的使用場景如下: 1. Executors.newFixedThreadPool() 創(chuàng)建一個定長的線程池,每提交一個任務(wù)就創(chuàng)建一個線程,直到達到池的**長度,這時線程池會保持長度不再變化。 當線程處于空閑狀態(tài)時,它們并不會被回收,除非線程池被關(guān)閉。
當所有的線程都處于活動狀態(tài)時,新任務(wù)都會處于等待狀態(tài),直到有線程空閑出來。 只有核心線程并且不會被回收,能夠更加快速的響應(yīng)外界的請求。 2. Executors.newCachedThreadPool() 創(chuàng)建一個可緩存的線程池,如果當前線程池的長度超過了處理的需要時,它可以靈活的回收空閑的線程,當需要增加時,它可以靈活的添加新的線程,而不會對池的長度作任何限制 線程數(shù)量不定的線程池,只有非核心線程,**線程數(shù)為 Integer.MAX_VALUE。
當線程池中的線程都處于活動狀態(tài)時,線程池會創(chuàng)建新的線程來處理新任務(wù),否則利用空閑的線程來處理新任務(wù)。線程池中的空閑線程具有超時機制,為 60s。 任務(wù)隊列相當于一個空**,導致任何任務(wù)都會立即被執(zhí)行,適合執(zhí)行大量耗時較少的任務(wù)。
當整個線程池都處于限制狀態(tài)時,線程池中的線程都會超時而被停止。 3. Executors.newScheduledThreadPool() 創(chuàng)建一個定長的線程池,而且支持定時的以及周期性的任務(wù)執(zhí)行,類似于Timer。 非核心線程數(shù)沒有限制,并且非核心線程閑置的時候立即回收,主要用于執(zhí)行定時任務(wù)和具有固定周期的重復(fù)任務(wù)。 4. Executors.newSingleThreadExecutor() 創(chuàng)建一個單線程化的executor,它只創(chuàng)建**的worker線程來執(zhí)行任務(wù) 只有一個核心線程,保證所有的任務(wù)都在一個線程中順序執(zhí)行,意義在于不需要處理線程同步的問題。
一般用于執(zhí)行后臺耗時任務(wù),當任務(wù)執(zhí)行完成會自動停止;同時由于它是一個服務(wù),優(yōu)先級要遠遠高于線程,更不容易被系統(tǒng)殺*,因此比較適合執(zhí)行一些高優(yōu)先級的后臺任務(wù)。 使用步驟:創(chuàng)建IntentService的子類,重寫onHandleIntent方法,在onHandleIntent中執(zhí)行耗時任務(wù) 原理:在源碼實現(xiàn)上,IntentService封裝了HandlerThread和Handler。onHandleIntent方法結(jié)束后會調(diào)用IntentService的stopSelf(int startId)方法嘗試停止服務(wù)。
IntentService的內(nèi)部是通過消息的方式請求HandlerThread執(zhí)行任務(wù),HandlerThread內(nèi)部又是一種使用Handler的Thread,這就意味著IntentService和Looper一樣是順序執(zhí)行后臺任務(wù)的 (HandlerThread:封裝了Handler + ThreadHandlerThread適合在有需要一個工作線程(非UI線程)+任務(wù)的等待隊列的形式,優(yōu)點是不會有堵塞,減少了對性能的消耗,缺點是不能同時進行多個任務(wù)的處理,需要等待進行處理。
目標和任務(wù)有什么區(qū)別?
目標和任務(wù)有什么區(qū)別? 目標是具體可量化的,由目的而生,**是達成目的的籌劃,而任務(wù)就是**中的每個完成點 一般先有目的,再有**,后有目標,用任務(wù)完成目標。 一個人要使自己的生活得有意義,就要樹立遠大的理想。
當然,你如果還沒有想好應(yīng)該怎么實行,會認為自己的目標定得很大,完成預(yù)定目標可能有困難。
你可以將大目標化解成幾個小目標,再將小目標一一完成。用這個方法能使你克服畏懼心理,(大目標不好完成)按照制定的**去做。 你在完成小目標時,要有自信心,事情不能半途而廢,要堅持做下去,要反復(fù)地認真地將一件小事做好。你要調(diào)整自己的心態(tài),你要不斷地鼓勵自己,相信自己一定能做好一件小事,只要你想認真完成的事,堅信你就會做好它。
等你有了完成一件事情的經(jīng)歷以后,及時總結(jié)經(jīng)驗,進一步增強自信心,向另一個目標進發(fā),全力以赴想方設(shè)法去圓滿完成它。 在完成任務(wù)時你就會感覺非常快樂。你就是一個充滿自信,最有作為的人!你還年輕,相信自己的能力,自己一定是個勝利者! 目標是主動的,任務(wù)是被動的。
目標是你某項任務(wù)的一個硬性指標,但他有長期和短期之分。任務(wù)是你目前所必需完成的。他們也有共性。
目標是你自己定的。任務(wù)可能是你自己定的也可能是別人派給你的。 目標 一、拼音: mù biāo 二、釋義: 1、射擊、攻擊或?qū)で蟮奈锛?/p>
2、想要達到的境界或目的。 三、造句: 1、一旦立下目標,不達目標絕不罷手,方可成功。 2、偉人之心常存目標;常人之心常存愿望。 3、目標要遠大,不達目的決不罷休。
4、進步,意味著目標不斷前移,階段不斷更新,它的視影不斷變化。 5、一個人沒目標,前途盡費;一個人沒理想,碌碌終生。 任務(wù) 一、拼音: rèn wù 二、釋義: 擔任的職務(wù)或使命。 三、造句: 1、救援船盡快地駛離軍港去執(zhí)行任務(wù)。
2、巡道工人披星戴月地執(zhí)行任務(wù)。 3、我們應(yīng)抓住有利時機,趁熱打鐵完成上級交給的任務(wù)。 4、播種保苗歷來是棉花生產(chǎn)最為關(guān)鍵的時期與任務(wù)。 5、毛主席語錄:政治的首要任務(wù)是區(qū)分敵友。
“任務(wù)”和“目標”這兩個詞有什么區(qū)別? 任務(wù)一般做被動的,應(yīng)該是別人給你訂任務(wù)。 目標一般做主動的。自己給自己設(shè)目標。
1、目標是具體可量化的,由目的而生。 2、任務(wù)就是**中的每個完成點。 3、一般先有目的,再有**,用任務(wù)完成目標。
目標是具體可量化的,由目的而生,**是達成目的的籌劃,而任務(wù)就是**中的每個完成點 一般先有目的,再有**,后有目標,用任務(wù)完成目標。 主線任務(wù)和支線任務(wù)有什么區(qū)別? 個人感覺主線給的東西以經(jīng)驗,SP居多 直線就是錢啊,東西的 程序與任務(wù)有什么區(qū)別 程序記錄了你電腦全部執(zhí)行的程式,包括任務(wù),**,系統(tǒng)程式等等。程序隨意結(jié)束會對電腦產(chǎn)生影響。ctrl+alt+del可以檢視 任務(wù)是你執(zhí)行電腦后開啟的應(yīng)用程式(比如IE,QQ,游戲啊==) 結(jié)束任務(wù)一般不會對電腦系統(tǒng)產(chǎn)生影響。
夜場ds做有任務(wù)和沒任務(wù)有什么區(qū)別 慎重:請所有的人看下 我在 上海做夜場的 上海有很多好的場子的 。但騙子更多 特別是那些什么***的**網(wǎng)站都是中介建立的 ***是不會自己建立網(wǎng)站的我大概給大家說一下情況 以免被騙 我做模特領(lǐng)隊 期待各位佳麗 **、無論真假 去面試的時候身上別帶很多現(xiàn)金。 第二、上海夜場的** 全都是領(lǐng)隊 自己招的 或者周圍的朋友 介紹的。
第三 如果你 去面試 他把你帶到什么**寫字樓 說是什么人事部的話 ,那么不要考慮 轉(zhuǎn)身就走,因為上海沒有一家 ***會因為招人這點小事 而專門設(shè)立個 人事部。 一、承諾不收任何押金和費用,公司客源穩(wěn)定。 二、宣告本廣告是網(wǎng)上**真正**女的廣告。 三、為了證實本**的真實性,你可以檢視網(wǎng)上**的廣告,看有沒有和我們一樣承諾不收取任何押金和費用的內(nèi)容。
說的再好不如親眼所見,為防止被騙,面試請晚上直接來場子,不需要帶一分錢,條件合格者,帶你先看看場子工作環(huán)境,和其他同類工作人員,然后自己考慮是否在此上班,完全出于自愿,絕不涉及任何違法工作內(nèi)容。 如果你對目前的工作不滿意,如果你想 *** 多賺一點錢,如果你對未來充滿憧憬,如果你喜歡夜生活,這里將給你的將來 多一個選擇和把握的機會。 非中介,本人承諾不收費,請記住 《要人的地方不要錢,要錢的地方不缺人》 android handler和非同步任務(wù)有什么區(qū)別 其實handler與非同步任務(wù)沒有可比性,您的基礎(chǔ)還需要加強。
下面這篇csdn的部落格中有handler的詳細介紹, :blog.csdn./androidwuyou/article/details/52601498 AsyncTask實現(xiàn)的原理和適用的優(yōu)缺點 AsyncTask,是android提供的輕量級的非同步類,可以直接繼承AsyncTask,在類中實現(xiàn)非同步操作,并提供介面反饋當前非同步執(zhí)行的程度(可以通過介面實現(xiàn)UI進度更新),**反饋執(zhí)行的結(jié)果給UI主執(zhí)行緒. 使用的優(yōu)點: 簡單,快捷 過程可控 使用的缺點百科: 在使用多個非同步操作和并需要進行Ui變更時,就變得復(fù)雜起來. Handler非同步實現(xiàn)的原理和適用的優(yōu)缺點 在Handler 非同步實現(xiàn)時,涉及到 Handler, Looper, Message,Thread四個物件,實現(xiàn)非同步的流程是主執(zhí)行緒啟動Thread(子執(zhí)行緒)執(zhí)行并生成Message-Looper獲取Message并傳遞給HandlerHandler逐個獲取Looper中的Message,并進行UI變更。 使用的優(yōu)點: 結(jié)構(gòu)清晰,功能定義明確 對于多個后臺任務(wù)時,簡單,清晰 使用的缺點: 在單個后臺非同步處理時,顯得程式碼過多,結(jié)構(gòu)過于復(fù)雜(相對性) AsyncTask介紹 Android的AsyncTask比Handler更輕量級一些(只是程式碼上輕量一些,而實際上要比handler更耗資源),適用于簡單的非同步處理。 首先明確Android之所以有Handler和AsyncTask,都是為了不阻塞主執(zhí)行緒(UI執(zhí)行緒),且UI的更新只能在主執(zhí)行緒中完成,因此非同步處理是不可避免的。 Android為了降低這個開發(fā)難度,提供了AsyncTask。AsyncTask就是一個封裝過的后臺任務(wù)類,顧名思義就是非同步任務(wù)。 AsyncTask直接繼承于Object類,位置為android.os.AsyncTask。要使用AsyncTask工作我們要提供三個泛型引數(shù),并重載幾個方法。
Android中的線程狀態(tài) – AsyncTask詳解
在操作系統(tǒng)中,線程是操作系統(tǒng)調(diào)度的最小單元,同時線程又是一種受限的系統(tǒng)資源,即線程不可能無限制地產(chǎn)生,并且 線程的創(chuàng)建和銷毀都會有相應(yīng)的開銷。 當系統(tǒng)中存在大量的線程時,系統(tǒng)會通過會時間片輪轉(zhuǎn)的方式調(diào)度每個線程,因此線程不可能做到**的并行。
如果在一個進程中頻繁地創(chuàng)建和銷毀線程,顯然不是高效的做法。
正確的做法是采用線程池,一個線程池中會緩存一定數(shù)量的線程,通過線程池就可以避免因為頻繁創(chuàng)建和銷毀線程所帶來的系統(tǒng)開銷。 AsyncTask是一個抽象類,它是由Android封裝的一個輕量級異步類(輕量體現(xiàn)在使用方便、代碼簡潔),它可以**程池中執(zhí)行后臺任務(wù),然后把執(zhí)行的進度和最終結(jié)果傳遞給主線程并在主線程中更新UI。 AsyncTask的內(nèi)部封裝了 兩個線程池 (SerialExecutor和THREAD_POOL_EXECUTOR)和 一個Handler (InternalHandler)。 其中 SerialExecutor線程池用于任務(wù)的排隊,讓需要執(zhí)行的多個耗時任務(wù),按順序排列 , THREAD_POOL_EXECUTOR線程池才真正地執(zhí)行任務(wù) , InternalHandler用于從工作線程切換到主線程 。
1.AsyncTask的泛型參數(shù) AsyncTask是一個抽象泛型類。 其中,三個泛型類型參數(shù)的含義如下: Params: 開始異步任務(wù)執(zhí)行時傳入的參數(shù)類型; Progress: 異步任務(wù)執(zhí)行過程中,返回下載進度值的類型; Result: 異步任務(wù)執(zhí)行完成后,返回的結(jié)果類型; 如果AsyncTask確定不需要傳遞具體參數(shù),那么這三個泛型參數(shù)可以用Void來代替。 有了這三個參數(shù)類型之后,也就控制了這個AsyncTask子類各個階段的返回類型,如果有不同業(yè)務(wù),我們就需要再另寫一個AsyncTask的子類進行處理。
2.AsyncTask的核心方法 onPreExecute() 這個方**在 后臺任務(wù)開始執(zhí)行之間調(diào)用,在主線程執(zhí)行。 用于進行一些界面上的初始化操作,比如顯示一個進度條對話框等。 doInBackground(Params…) 這個方法中的所有代碼都會 在子線程中運行,我們應(yīng)該在這里去處理所有的耗時任務(wù)。
任務(wù)一旦完成就可以通過return語句來將任務(wù)的執(zhí)行結(jié)果進行返回,如果AsyncTask的第三個泛型參數(shù)指定的是Void,就可以不返回任務(wù)執(zhí)行結(jié)果。 注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務(wù)的執(zhí)行進度,可以調(diào)用publishProgress(Progress…)方法來完成。 onProgressUpdate(Progress…) 當在后臺任務(wù)中調(diào)用了publishProgress(Progress…)方法后,這個方法就很快會被調(diào)用,方法中攜帶的參數(shù)就是在后臺任務(wù)中傳遞過來的。
在這個方法中可以對UI進行操作,在主線程中進行,利用參數(shù)中的數(shù)值就可以對界面元素進行相應(yīng)的更新。 onPostExecute(Result) 當doInBackground(Params…)執(zhí)行完畢并通過return語句進行返回時,這個方法就很快會被調(diào)用。返回的數(shù)據(jù)會作為參數(shù)傳遞到此方法中, 可以利用返回的數(shù)據(jù)來進行一些UI操作,在主線程中進行,比如說提醒任務(wù)執(zhí)行的結(jié)果,以及關(guān)閉掉進度條對話框等。 上面幾個方法的調(diào)用順序: onPreExecute() –> doInBackground() –> publishProgress() –> onProgressUpdate() –> onPostExecute() 如果不需要執(zhí)行更新進度則為onPreExecute() –> doInBackground() –> onPostExecute(), 除了上面四個方法,AsyncTask還提供了onCancelled()方法, 它同樣在主線程中執(zhí)行,當異步任務(wù)取消時,onCancelled()會被調(diào)用,這個時候onPostExecute()則不會被調(diào)用 ,但是要注意的是, AsyncTask中的cancel()方法并不是真正去取消任務(wù),只是設(shè)置這個任務(wù)為取消狀態(tài),我們需要在doInBackground()判斷終止任務(wù)。
就好比想要終止一個線程,調(diào)用interrupt()方法,只是進行標記為中斷,需要**程內(nèi)部進行標記判斷然后中斷線程。 3.AsyncTask的簡單使用 這里我們模擬了一個下載任務(wù),在doInBackground()方法中去執(zhí)行具體的下載邏輯,在onProgressUpdate()方法中顯示當前的下載進度,在onPostExecute()方法中來提示任務(wù)的執(zhí)行結(jié)果。如果想要啟動這個任務(wù),只需要簡單地調(diào)用以下代碼即可: 4.使用AsyncTask的注意事項 ①異步任務(wù)的實例必須在UI線程中創(chuàng)建,即AsyncTask對象必須在UI線程中創(chuàng)建。 ②execute(Params… params)方法必須在UI線程中調(diào)用。
③不要手動調(diào)用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)這幾個方法。 ④不能在doInBackground(Params… params)中更改UI組件的信息。 ⑤一個任務(wù)實例只能執(zhí)行一次,如果執(zhí)行第二次將會拋出異常。 先從初始化一個AsyncTask時,調(diào)用的構(gòu)造函數(shù)開始分析。
這段代碼雖然看起來有點長,但實際上并沒有任何具體的邏輯會得到執(zhí)行,只是初始化了兩個變量,mWorker和mFuture,并在初始化mFuture的時候?qū)Worker作為參數(shù)傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象,這兩個變量會暫時保存在內(nèi)存中,稍后才會用到它們。 FutureTask實現(xiàn)了Runnable接口,關(guān)于這部分內(nèi)容可以看這篇文章。
mWorker中的call()方法執(zhí)行了耗時操作,即result = doInBackground(mParams);,然后把執(zhí)行得到的結(jié)果通過postResult(result);,傳遞給內(nèi)部的Handler跳轉(zhuǎn)到主線程中。在這里這是實例化了兩個變量,并沒有開啟執(zhí)行任務(wù)。 那么mFuture對象是怎么加載到線程池中,進行執(zhí)行的呢? 接著如果想要啟動某一個任務(wù),就需要調(diào)用該任務(wù)的execute()方法,因此現(xiàn)在我們來看一看execute()方法的源碼,如下所示: 調(diào)用了executeOnExecutor()方法,具體執(zhí)行邏輯在這個方法里面: 可以 看出,先執(zhí)行了onPreExecute()方法,然后具體執(zhí)行耗時任務(wù)是在exec.execute(mFuture),把構(gòu)造函數(shù)中實例化的mFuture傳遞進去了。
exec具體是什么? 從上面可以看出具體是sDefaultExecutor,再追溯看到是SerialExecutor類,具體源碼如下: 終于追溯到了調(diào)用了SerialExecutor 類的execute方法。SerialExecutor 是個靜態(tài)內(nèi)部類,是所有實例化的AsyncTask對象公有的,SerialExecutor 內(nèi)部維持了一個隊列,通過鎖使得該隊列保證AsyncTask中的任務(wù)是串行執(zhí)行的,即多個任務(wù)需要一個個加到該隊列中,然后執(zhí)行完隊列頭部的再執(zhí)行下一個,以此類推。 在這個方法中,有兩個主要步驟。 ①向隊列中加入一個新的任務(wù),即之前實例化后的mFuture對象。
②調(diào)用 scheduleNext()方法,調(diào)用THREAD_POOL_EXECUTOR執(zhí)行隊列頭部的任務(wù)。 由此可見SerialExecutor 類僅僅為了保持任務(wù)執(zhí)行是串行的,實際執(zhí)行交給了THREAD_POOL_EXECUTOR。 THREAD_POOL_EXECUTOR又是什么? 實際是個線程池,開啟了一定數(shù)量的核心線程和工作線程。
然后調(diào)用線程池的execute()方法。執(zhí)行具體的耗時任務(wù),即開頭構(gòu)造函數(shù)中mWorker中call()方法的內(nèi)容。先執(zhí)行完doInBackground()方法,又執(zhí)行postResult()方法,下面看該方法的具體內(nèi)容: 該方法向Handler對象發(fā)送了一個消息,下面具體看AsyncTask中實例化的Hanlder對象的源碼: 在InternalHandler 中,如果收到的消息是MESSAGE_POST_RESULT,即執(zhí)行完了doInBackground()方法并傳遞結(jié)果,那么就調(diào)用finish()方法。 如果任務(wù)已經(jīng)取消了,回調(diào)onCancelled()方法,否則回調(diào) onPostExecute()方法。
如果收到的消息是MESSAGE_POST_PROGRESS,回調(diào)onProgressUpdate()方法,更新進度。 InternalHandler是一個靜態(tài)類,為了能夠?qū)?zhí)行環(huán)境切換到主線程,因此這個類必須在主線程中進行加載。所以變相要求AsyncTask的類必須在主線程中進行加載。
到此為止,從任務(wù)執(zhí)行的開始到結(jié)束都從源碼分析完了。 AsyncTask的串行和并行 從上述源碼分析中分析得到,默?。
android中同時使用asynctask和handler有沖突嗎
Android的AsyncTask類中使用自定義handler 熟知的AsyncTask就是Thread+handler實現(xiàn),但是這個handler并不能很好的被我們控制。在我的需求中希望在AsyncTask中使用自定義的handler,而且在必要的時候退出消息循環(huán)。
實現(xiàn)思路如下: AsyncTask中創(chuàng)建消息循環(huán),然后啟動handler目標操作,當操作結(jié)束后,結(jié)束消息循環(huán),執(zhí)行后續(xù)代碼。
Android進程間和線程間通信方式
?進程:是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)**上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。 線程:是進程的一個實體,是CPU調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
線程自己基本上不擁有系統(tǒng)資源,只擁有一些在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
區(qū)別: (1)、一個程序至少有一個進程,一個進程至少有一個線程; (2)、線程的劃分尺度小于進程,使得多線程程序的并發(fā)性高; (3)、進程在執(zhí)行過程中擁有獨立的內(nèi)存單元,而多個線程共享內(nèi)存,但線程之間沒有單獨的地址空間,一個線程*掉就等于整個進程*掉。 ——————— 一、Android進程間通信方式 1.Bundle 由于Activity,Service,Receiver都是可以通過Intent來攜帶Bundle傳輸數(shù)據(jù)的,所以我們可以在一個進程中通過Intent將攜帶數(shù)據(jù)的Bundle發(fā)送到另一個進程的組件。 缺點:無法傳輸Bundle不支持的數(shù)據(jù)類型。 2.ContentProvider ContentProvider是Android四大組件之一,以表格的方式來儲存數(shù)據(jù),提供給外界,即Content Provider可以跨進程訪問其他應(yīng)用程序中的數(shù)據(jù)。
用法是繼承ContentProvider,實現(xiàn)onCreate,query,update,insert,delete和getType方法,onCreate是負責創(chuàng)建時做一些初始化的工作,增刪查改的方法就是對數(shù)據(jù)的查詢和修改,getType是返回一個String,表示Uri請求的類型。注冊完后就可以使用ContentResolver去請求指定的Uri。 3.文件 兩個進程可以到同一個文件去交換數(shù)據(jù),我們不僅可以保存文本文件,還可以將對象持久化到文件,從另一個文件恢復(fù)。
要注意的是,當并發(fā)讀/寫時可能會出現(xiàn)并發(fā)的問題。 4.Broadcast Broadcast可以向android系統(tǒng)中所有應(yīng)用程序發(fā)送廣播,而需要跨進程通訊的應(yīng)用程序可以監(jiān)聽這些廣播。 5.AIDL方式 Service和Content Provider類似,也可以訪問其他應(yīng)用程序中的數(shù)據(jù),Content Provider返回的是Cursor對象,而Service返回的是Java對象,這種可以跨進程通訊的服務(wù)叫AIDL服務(wù)。
AIDL通過定義服務(wù)端暴露的接口,以提供給客戶端來調(diào)用,AIDL使服務(wù)器可以并行處理,而Messenger封裝了AIDL之后只能串行運行,所以Messenger一般用作消息傳遞。 6.Messenger Messenger是基于AIDL實現(xiàn)的,服務(wù)端(被動方)提供一個Service來處理客戶端(主動方)連接,維護一個Handler來創(chuàng)建Messenger,在onBind時返回Messenger的binder。 雙方用Messenger來發(fā)送數(shù)據(jù),用Handler來處理數(shù)據(jù)。
Messenger處理數(shù)據(jù)依靠Handler,所以是串行的,也就是說,Handler接到多個message時,就要排隊依次處理。 7.Socket Socket方法是通過**來進行數(shù)據(jù)交換,注意的是要在子線程請求,不然會堵塞主線程。客戶端和服務(wù)端建立連接之后即可不斷傳輸數(shù)據(jù),比較適合實時的數(shù)據(jù)傳輸 二、Android線程間通信方式 一般說線程間通信主要是指主線程(也叫UI線程)和子線程之間的通信,主要有以下兩種方式: 1.AsyncTask機制 AsyncTask,異步任務(wù),也就是說在UI線程運行的時候,可以在后臺的執(zhí)行一些異步的操作;AsyncTask可以很容易且正確地使用UI線程,AsyncTask允許進行后臺操作,并在不顯示使用工作線程或Handler機制的情況下,將結(jié)果反饋給UI線程。但是AsyncTask只能用于短時間的操作(最多幾秒就應(yīng)該結(jié)束的操作),如果需要長時間運行在后臺,就不適合使用AsyncTask了,只能去使用Java提供的其他API來實現(xiàn)。
2.Handler機制 Handler,繼承自O(shè)bject類,用來發(fā)送和處理Message對象或Runnable對象;Handler在創(chuàng)建時會與當前所在的線程的Looper對象相關(guān)聯(lián)(如果當前線程的Looper為空或不存在,則會拋出異常,此時需要**程中主動調(diào)用Looper.prepare()來創(chuàng)建一個Looper對象)。使用Handler的主要作用就是在后面的過程中發(fā)送和處理Message對象和讓其他的線程完成某一個動作(如在工作線程中通過Handler對象發(fā)送一個Message對象,讓UI線程進行UI的更新,然后UI線程就會在MessageQueue中得到這個Message對象(取出Message對象是由其相關(guān)聯(lián)的Looper對象完成的),并作出相應(yīng)的響應(yīng))。 三、Android兩個子線程之間通信 面試的過程中,有些面試官可能會問Android子線程之間的通信方式,由于絕大部分程序員主要關(guān)注的是Android主線程和子線程之間的通信,所以這個問題很容易讓人懵逼。 主線程和子線程之間的通信可以通過主線程中的handler把子線程中的message發(fā)給主線程中的looper,或者,主線程中的handler通過post向looper中發(fā)送一個runnable。
但looper默認存在于main線程中,子線程中沒有Looper,該怎么辦呢?其實原理很簡單,把looper綁定到子線程中,并且創(chuàng)建一個handler。在另一個線程中通過這個handler發(fā)送消息,就可以實現(xiàn)子線程之間的通信了。
關(guān)于 Handler 的這 20 個問題,你都清楚嗎?
Android 11?開始,AsyncTask?正式謝幕,變成了不推薦使用的 API。**建議采用?Kotlin 協(xié)程替代,或者自行實現(xiàn)。
事實上,無論是 AsyncTask 還是協(xié)程,背后都有 Handler 的功勞。
無論從普及原理的角度、還是從自行實現(xiàn)的角度,我們都需要吃透這個 Android 系統(tǒng)所特有的線程間通信方式Handler?機制! 初嘗 Handler 機制的時候,原以為 Handler 類發(fā)揮了很大的作用。當你深入了解它的原理之后,會發(fā)現(xiàn) Handler 只是該機制的 調(diào)用入口和回調(diào) 而已,最重要的東西是?Looper?和?MessagQueue,以及不斷流轉(zhuǎn)的?Message。 本次針對 Handler 機制常被提及和容易困擾的?20 個問題進行整理和回答,供大家解惑和回顧~ 簡述下 Handler 機制的總體原理? Looper 存在哪?如何可以保證線程獨有? 如何理解 ThreadLocal 的作用? 主線程 Main Looper 和一般 Looper 的異同? Handler 或者說 Looper 如何切換線程? Looper 的 loop() *循環(huán)為什么不卡*? Looper 的等待是如何能夠準確喚醒的? Message 如何獲?。繛槭裁催@么設(shè)計? MessageQueue 如何管理 Message? 理解 Message 和 MessageQueue 的異同? Message 的執(zhí)行時刻如何管理? Handler、Mesage 和 Runnable 的關(guān)系如何理解? IdleHandler 空閑 Message 了解過嗎?有什么用? 異步 Message 或同步屏障了解過嗎?怎么用?什么原理? Looper 和 MessageQueue、Message 及 Handler 的關(guān)系? Native 側(cè)的 NativeMessageQueue 和 Looper 的作用是? Native 側(cè)如何使用 Looper? Handler 為什么可能導致內(nèi)存泄露?如何避免? Handler 在系統(tǒng)當中的應(yīng)用 Android 為什么不允許并發(fā)訪問 UI? 1. 簡述下 Handler 機制的總體原理? Looper 準備和開啟輪循: 尚無 Message 的話,調(diào)用 Native 側(cè)的?pollOnce()?進入 無限等待 存在 Message,但執(zhí)行時間?when?尚未滿足的話,調(diào)用 pollOnce() 時傳入剩余時長參數(shù)進入 有限等待 Looper#prepare()?初始化線程獨有的?Looper?以及?MessageQueue Looper#loop()?開啟 *循環(huán) 讀取 MessageQueue 中下一個滿足執(zhí)行時間的 Message Message 發(fā)送、入隊和出隊: Native 側(cè)如果處于無限等待的話:任意線程向?Handler?發(fā)送?Message?或?Runnable?后,Message 將按照 when 條件的先后,**入 Handler 持有的 Looper 實例所對應(yīng)的 MessageQueue 中 適當?shù)奈恢?。MessageQueue 發(fā)現(xiàn)有合適的 Message 插入后將調(diào)用 Native 側(cè)的?wake()?喚醒無限等待的線程。
這將促使 MessageQueue 的讀取繼續(xù) 進入下一次循環(huán) ,此刻 Queue 中已有滿足條件的 Message 則出隊返回給 Looper Native 側(cè)如果處于有限等待的話:在等待指定時長后 epoll_wait 將返回。線程繼續(xù)讀取 MessageQueue,此刻因為時長條件將滿足將其出隊 Looper 處理 Message 的實現(xiàn): Looper 得到 Message 后回調(diào) Message 的?callback?屬性即 Runnable,或依據(jù)?target?屬性即 Handler,去執(zhí)行 Handler 的回調(diào)。 存在?mCallback?屬性的話回調(diào)?Handler$Callback 反之,回調(diào)?handleMessage() 2. Looper 存在哪?如何可以保證線程獨有? Looper 實例被管理在靜態(tài)屬性?sThreadLocal?中 ThreadLocal?內(nèi)部通過?ThreadLocalMap?持有 Looper,key?為 ThreadLocal 實例本身,value?即為 Looper 實例 每個 Thread 都有一個自己的 ThreadLocalMap,這樣可以保證每個線程對應(yīng)一個獨立的 Looper 實例,進而保證?myLooper()?可以獲得線程獨有的 Looper 彩蛋:一個 App 擁有幾個 Looper 實例?幾個 ThreadLocal 實例?幾個 MessageQueue 實例?幾個 Message 實例?幾個 Handler 實例 一個線程只有一個 Looper 實例 一個 Looper 實例只對應(yīng)著一個 MessageQueue 實例 一個 MessageQueue 實例可對應(yīng)多個 Message 實例,其從 Message 靜態(tài)池里獲取,存在 50 的上限 一個線程可以擁有多個 Handler 實例,其Handler 只是發(fā)送和執(zhí)行任務(wù)邏輯的入口和出口 ThreadLocal 實例是靜態(tài)的,整個進程 共用一個實例 。
每個 Looper 存放的 ThreadLocalMap 均弱引用它作為 key 3. 如何理解 ThreadLocal 的作用? 首先要明確并非不是用來切換線程的, 只是為了讓每個線程方便程獲取自己的 Looper 實例 ,見 Looper#myLooper() 后續(xù)可供 Handler 初始化時 指定其所屬的 Looper 線程 也可用來線程判斷自己是否 是主線程 4. 主線程 Main Looper 和一般 Looper 的異同? 區(qū)別: Main Looper?不可?quit 主線程需要不斷讀取系統(tǒng)消息和用書輸入,是進程的入口,只可被系統(tǒng)直接終止。進而其 Looper 在創(chuàng)建的時候設(shè)置了 不可quit的標志 ,而 其他線程的 Looper 則可以也必須手動 quit Main Looper 實例還被 靜態(tài)緩存 為了便于每個線程獲得主線程 Looper 實例,見 Looper#getMainLooper(),Main Looper 實例還作為?sMainLooper?屬性緩存到了 Looper 類中。 相同點: 都是通過 Looper#prepare() 間接調(diào)用 Looper 構(gòu)造函數(shù)創(chuàng)建的實例 都被靜態(tài)實例 ThreadLocal 管理,方便每個線程獲取自己的 Looper 實例 彩蛋:主線程為什么不用初始化 Looper? App 的入口并非 MainActivity,也不是 Application,而是 ActivityThread。
其為了 Application、ContentProvider、Activity 等組件的運行,必須事先啟動不停接受輸入的 Looper 機制,所以在 main() 執(zhí)行的**將調(diào)用 prepareMainLooper() 創(chuàng)建 Looper 并調(diào)用 loop() 輪循。 不需要我們調(diào)用,也不可能有我們調(diào)用。 可以說如果主線程沒有創(chuàng)建 Looper 的話,我們的組件也不可能運行得到! 5. Handler 或者說 Looper 如何切換線程? Handler 創(chuàng)建的時候指定了其所屬線程的 Looper,進而持有了 Looper 獨有的 MessageQueue Looper#loop() 會持續(xù)讀取 MessageQueue 中合適的 Message,沒有 Message 的時候進入等待 當向 Handler 發(fā)送 Message 或 Runnable 后,會向持有的 MessageQueue 中插入 Message Message 抵達并滿足條件后會喚醒 MessageQueue 所屬的線程,并將 Message 返回給 Looper Looper 接著回調(diào) Message 所指向的 Handler Callback 或 Runnable,達到線程切換的目的 簡言之,向 Handler 發(fā)送 Message 其實是向 Handler 所屬線程的獨有 MessageQueue 插入 Message。
而線程獨有的 Looper 又會持續(xù)讀取該 MessageQueue。所以向其他線程的 Handler 發(fā)送完 Message,該線程的 Looper 將自動響應(yīng)。 6. Looper 的 loop() *循環(huán)為什么不卡*? 為了讓主線程持續(xù)處理用戶的輸入,loop() 是 *循環(huán) ,持續(xù)調(diào)用 MessageQueue#next()?讀取合適的 Message。 但當沒有 Message 的時候,會調(diào)用?pollOnce()?并通過 Linux 的?epoll?機制進入等待并釋放資源。
同時?eventFd?會監(jiān)聽 Message 抵達的寫入事件并進行喚醒。 這樣可以 空閑時釋放資源、不卡*線程,同時能持續(xù)接收輸入的目的 。 彩蛋1:loop() 后的處理為什么不可執(zhí)行 因為 loop() 是*循環(huán),直到 quit 前后面的處理無法得到執(zhí)行,所以避免將處理放在 loop() 的后面。 彩蛋2:Looper 等待的時候線程到底是什么狀態(tài)? 調(diào)用 Linux 的 epoll 機制進入 等待 ,事實上 Java 側(cè)打印該線程的狀態(tài),你會發(fā)現(xiàn)線程處于?Runnable?狀態(tài),只不過 CPU 資源被暫時釋放。
7. Looper 的等待是如何能夠準確喚醒的? 讀取合適 Message 的 MessageQueue#next() 會因為 Message 尚無或執(zhí)行條件尚未滿足進行兩種等的等待: 無限等待 尚無 Message(隊列中沒有 Message 或建立了同步屏障但尚無異步 Message)的時候,調(diào)用 Natvie 側(cè)的?pollOnce()?會傳入?yún)?shù)?-1 。 Linux 執(zhí)行?epoll_wait()?將進入無限等待,其等待合適的 Message 插入后調(diào)用 Native 側(cè)的?wake()?向喚醒 fd 寫入事件觸發(fā)喚醒 MessageQueue 讀取的下一次循環(huán) 有限等待 有限等待的場合將下一個 Message?剩余時長作為參數(shù) 交給 epoll_wait(),epoll 將等待一段時間之后 自動返回 ,接著回到 MessageQueue 讀取的下一次循環(huán) 8. Message 如何獲???為什么這么設(shè)計? 享元設(shè)計模式:通過 Message 的靜態(tài)方法?obatin()?獲取,因為該方法不是無腦地 new,而是 從單鏈表池子里獲取實例 ,并在?recycle()?后將其放回池子 好處在于復(fù)用 Message 實例,滿足頻繁使用 Message 的場景,更加高效 當然,緩存池存在上限?50 ,因為沒必要無限制地緩存,這本身也是一種浪費 需要留意緩存池是靜態(tài)的,也就是整個進程共用一個緩存池 9. MessageQueue 如何管理 Message? MessageQueue 通過單鏈表管理 Message,不同于進程共用的 Message Pool,其是線程獨有的 通過 Message 的執(zhí)行時刻 when 對 Message 進行排隊和出隊 MessageQueue 除了管理 Message,還要管理空閑 Handler 和 同步屏障 10. 理解 Message 和 MessageQueue 的異同? 相同點:都是通過 單鏈表來管理 Message 實例; Message 通過?obtain() 和 recycle() 向單鏈表獲取插入節(jié)點 MessageQueue 通過?enqueueMessage() 和 next() 向單鏈表獲取和插入節(jié)點 區(qū)別: Message 單鏈表是 靜態(tài)的,供進程使用的緩存池 MessageQueue 單鏈表 非靜態(tài),只供 Looper 線程使用 11. Message 的執(zhí)行時刻如何管理? 發(fā)送的 Message 都是按照執(zhí)行時刻?when?屬性的先后管理在 MessageQueue 里 延時 Message 的 when 等于調(diào)用的當前時刻和?delay?之和 非延時 Message 的 when 等于當前時刻(delay 為?0) 插隊 Message 的 when 固定為?0,便于插入隊列的?head 之后 MessageQueue 會根據(jù) 讀取的時刻和 when 進行比較 將 when 已抵達的出隊, 尚未抵達的計算出 當前時刻和目標 when 的插值 ,交由 Native 等待對應(yīng)的時長,時間到了自動喚醒繼續(xù)進行 Message 的讀取 事實上,無論上述哪種 Message 都不能保證在其對應(yīng)的 when 時刻執(zhí)行,往往都會延遲一些!因為必須等當前執(zhí)行的 Message 處理完了才有機會讀取隊列的下一個 Message。 比如發(fā)送了非延時 Message,when 即為發(fā)送的時刻,可它們不會立即執(zhí)行。都要等主線程現(xiàn)有的任務(wù)(Message)走完才能有機會出隊,而當這些任務(wù)執(zhí)行完 when 的時刻已經(jīng)過了。
假使隊列的前面還有其他 Message 的話,延遲會更加明顯! 彩蛋:. onCreate() 里向 Handler 發(fā)送大量 Message 會導致主線程卡頓嗎? 不會,發(fā)送的大量 Message 并非立即執(zhí)行,只是先放到隊列當中而已。 onCreate() 以及之后同步調(diào)用的 onStart() 和 onResume() 處理,本質(zhì)上也是 Message。等這個 Message 執(zhí)行完之后,才會進行讀取 Message 的下一次循環(huán),這時候才能回調(diào) onCreate 里發(fā)送的 Message。
需要說明的是,如果發(fā)送的是 FrontOfQueue 將 Message 插入隊首也不會立即先執(zhí)行,因為 onStart 和 onResume 是 onCreate 之后同步調(diào)用的,本質(zhì)上是同一個 Message 的作業(yè)周期 12. Handler、Mesage 和 Runnable 的關(guān)系如何理解? 作為使用 Handler 機制的入口, Handler 是發(fā)送 Message 或 Runnable 的起點 發(fā)送的?Runnable 本質(zhì)上也是 Message ,只不過作為?callback?屬性被持有 Handler 作為?target?屬性被持有在 Mesage 中 ,在 Message 執(zhí)行條件滿足的時候供 Looper 回調(diào) 事實上,Handler 只是供 App 使用 Handler 機制的 API,實質(zhì)來說,Message 是更為重要的載體。 13. IdleHandler 空閑 Message 了解過嗎?有什么用? 適用于期望 空閑時候執(zhí)行,但不影響主線程操作 的任務(wù) 系統(tǒng)應(yīng)用: Activity?destroy?回調(diào)就放在了?IdleHandler?中 ActivityThread?中?GCHandler?使用了 IdleHandler,在空閑的時候執(zhí)行?GC 操作 App 應(yīng)用: 發(fā)送一個返回 true 的 IdleHandler,在里面讓某個 View 不停閃爍,這樣當用戶發(fā)呆時就可以誘導用戶點擊這個 View 將某部分初始化放在 IdleHandler 里不影響 Activity 的啟動 14. 異步 Message 或同步屏障了解過嗎?怎么用?什么原理? 異步 Message:設(shè)置了?isAsync?屬性的 Message 實例 可以用異步 Handler 發(fā)送 也可以調(diào)用 Message#setAsynchronous()?直接設(shè)置為異步 Message 同步屏障:在 MessageQueue 的 某個位置放一個 target 屬性為 null 的 Message ,確保此后的非異步 Message 無法執(zhí)行,只能執(zhí)行異步 Message 原理:當 MessageQueue 輪循 Message 時候 發(fā)現(xiàn)建立了同步屏障的時候,會去跳過其他 Message,讀取下個 async 的 Message 并執(zhí)行,屏障移除之前同步 Message 都會被阻塞 應(yīng)用:比如 屏幕刷新?Choreographer?就使用到了同步屏障 ,確保屏幕刷新事件不會因為隊列負荷影響屏幕及時刷新。 注意: 同步屏障的添加或移除 API 并未對外公開,App 需要使用的話需要依賴反射機制 15. Looper 和 MessageQueue、Message 及 Handler 的關(guān)系? Message 是承載任務(wù)的載體,在 Handler 機制中貫穿始終 Handler 則是對外公開的 API,負責發(fā)送 Message 和處理任務(wù)的回調(diào),是?Message 的生產(chǎn)者 MessagQueue 負責管理待處理 Message 的入隊和出隊,是?Message 的容器 Looper 負責輪循 MessageQueue,保持線程持續(xù)運行任務(wù),是?Message 的消費者 彩蛋:如何保證 MessageQueue 并發(fā)訪問安全? 任何線程都可以通過 Handler 生產(chǎn) Message 并放入 MessageQueue 中,可 Queue 所屬的 Looper 在持續(xù)地讀取并嘗試消費 Message。
如何保證兩者不產(chǎn)生*鎖? Looper 在消費 Message 之前要先拿到 MessageQueue 的鎖, 只不過沒有 Message 或 Message 尚未滿足條件的進行等待前會事先釋放鎖 ,具體在于 nativePollOnce() 的調(diào)用在 synchronized 方法塊的外側(cè)。 Message 入隊前也需先拿到 MessageQueue 的鎖,而這時 Looper 線程正在等待且不持有鎖,可以確保 Message 的成功入隊。入隊后執(zhí)行喚醒后釋放鎖,Native 收到 event 寫入后恢復(fù) MessagQueue 的讀取并可以拿到鎖,成功出隊。 這樣一種在沒有 Message 可以消費時執(zhí)行等待同時不占著鎖的機制,避免了生產(chǎn)和消費的*鎖。
16. Native 側(cè)的 NativeMessageQueue 和 Looper 的作用是? NativeMessageQueue 負責連接 Java 側(cè)的 MessageQueue,進行后續(xù)的?wait?和?wake,后續(xù)將創(chuàng)建 wake 的FD,并通過 epoll 機制等待或喚醒。 但并不參與管理 Java 的 Message Native 側(cè)也需要 Looper 機制,等待和喚醒的需求是同樣的,所以將這部分實現(xiàn)都封裝到了 JNI 的NativeMessageQueue 和 Native 的 Looper 中, 供 Java 和 Native 一起使用 17. Native 側(cè)如何使用 Looper? Looper Native 部分承擔了 Java 側(cè) Looper 的等待和喚醒,除此之外其還提供了 Message、MessageHandler?或?WeakMessageHandler、LooperCallback?或?SimpleLooperCallback?等 API 這些部分可供 Looper 被 Native 側(cè)直接調(diào)用,比如?InputFlinger?廣泛使用了 Looper 主要方法是調(diào)用 Looper 構(gòu)造函數(shù)或 prepare 創(chuàng)建 Looper,然后通過 poll 開始輪詢,接著?sendMessage?或?addEventFd,等待 Looper 的喚醒。 使用過程和 Java 的調(diào)用思路類似 18. Handler 為什么可能導致內(nèi)存泄露?如何避免? 持有 Activity 實例的內(nèi)名內(nèi)部類或內(nèi)部類的 生命周期 應(yīng)當和 Activity 保持一致,否則產(chǎn)生內(nèi)存泄露的風險。
如果 Handler 使用不當,將造成不一致,表現(xiàn)為:匿名內(nèi)部類或內(nèi)部類寫法的 Handler、Handler$Callback、Runnable,或者Activity 結(jié)束時仍有活躍的 Thread 線程或 Looper 子線程 具體在于:異步任務(wù)仍然活躍或通過發(fā)送的 Message 尚未處理完畢,將使得內(nèi)部類實例的 生命周期被錯誤地延長 。造成本該回收的 Activity 實例 被別的?Thread?或?Main Looper?占據(jù)而無法及時回收 (活躍的 Thread 或 靜態(tài)屬性 sMainLooper 是 GC Root 對象) 建議的做法: 無論是 Handler、Handler$Callback 還是 Runnable,盡量采用 靜態(tài)內(nèi)部類 + 弱引用 的寫法,確保盡管發(fā)生不當引用的時候也可以因為弱引用能清楚持有關(guān)系 另外在 Activity 銷毀的時候及時地 終止 Thread、停止子線程的 Looper 或清空 Message ,確保徹底切斷 Activity 經(jīng)由 Message 抵達 GC Root 的引用源頭(Message 清空后會其與 Handler 的引用關(guān)系,Thread 的終止將結(jié)束其 GC Root 的源頭) 注意:靜態(tài)的 sThreadLocal 實例不持有存放 Looper 實例的 ThreadLocalMap,而是由 Thread 持有。從這個角度上來講,Looper 會被活躍的 GC Root Thread 持有,進而也可能導致內(nèi)存泄露。 彩蛋:網(wǎng)傳的 Handler$Callback 方案能否解決內(nèi)存泄露? 不能。
Callback 采用內(nèi)部類或匿名內(nèi)部類寫法的話,默認持有 Activity 的引用,而 Callback 被 Handler 持有。這最終將導致 Message -> Handler -> Callback -> Activity 的鏈條仍然存在。 19. Handler 在系統(tǒng)當中的應(yīng)用 特別廣泛,比如: Activity 生命周期的管理 屏幕刷新 HandlerThread、IntentService AsyncTask 等。
主要利用 Handler 的切換線程、主線程異步 Message 的重要特性。注意:Binder 線程非主線程,但很多操作比如生命周期的管理?。