面試:Handler 的工作原理是怎樣的四苇?

面試場景

平時(shí)開發(fā)用到其他線程嗎?都是如何處理的方咆?

基本都用 RxJava 的線程調(diào)度切換月腋,嗯對,就是那個(gè) observeOnsubscribeOn 可以直接處理,比如網(wǎng)絡(luò)操作榆骚,RxJava 提供了一個(gè)叫 io 線程的處理片拍。

在 RxJava 的廣泛使用之前,有使用過其他操作方式嗎妓肢?比如 Handler 什么的穆碎?

當(dāng)然用過呀。

那你講講 Handler 的工作原理吧职恳。

Handler 工作流程基本包括 Handler所禀、Looper、Message放钦、MessageQueue 四個(gè)部分色徘。但我們在日常開發(fā)中,經(jīng)常都只會用到 Handler 和 Message 兩個(gè)類操禀。Message 負(fù)責(zé)消息的搭載褂策,里面有個(gè) target 用于標(biāo)記消息,obj 用于存放內(nèi)容颓屑,Handler 負(fù)責(zé)消息的分發(fā)和處理斤寂。

一般在開發(fā)中是怎么使用 Handler 的?

官方不允許在子線程中更新 UI揪惦,所以我們經(jīng)常會把需要更新 UI 的消息直接發(fā)給處理器 Handler遍搞,通過重寫 Handler 的 handleMessage() 方法進(jìn)行 UI 的相關(guān)操作。

那使用中就沒什么需要注意的嗎器腋?

有溪猿,Handler 如果設(shè)置為私有變量的話,Android Studio 會報(bào)警告纫塌,提示可能會造成內(nèi)存泄漏诊县,這種情況可以通過設(shè)置為靜態(tài)內(nèi)部類 + 弱引用,或者在 onDestroy() 方法中調(diào)用 Handler.removeCallbacksAndMessages(null) 即可避免措左;

正文

總的來說這位面試的童鞋答的其實(shí)還是沒那么差依痊,不過細(xì)節(jié)程度還不夠,所以南塵就來帶大家一起走進(jìn) Handler怎披。

Handler 工作流程淺析

異步通信準(zhǔn)備 => 消息入隊(duì) => 消息循環(huán) => 消息處理

  1. 異步通信準(zhǔn)備
    假定是在主線程創(chuàng)建 Handler胸嘁,則會直接在主線程中創(chuàng)建處理器對象 Looper、消息隊(duì)列對象 MessageQueue 和 Handler 對象钳枕。需要注意的是缴渊,LooperMessageQueue 均是屬于其 創(chuàng)建線程 的。Looper 對象的創(chuàng)建一般通過 Looper.prepareMainLooper()Looper.prepare() 兩個(gè)方法鱼炒,而創(chuàng)建 Looper 對象的同時(shí),將會自動創(chuàng)建 MessageQueue蝌借,創(chuàng)建好 MessageQueue 后昔瞧,Looper 將自動進(jìn)入消息循環(huán)指蚁。此時(shí),Handler 自動綁定了主線程的 LooperMessageQueue自晰。

  2. 消息入隊(duì)
    工作線程通過 Handler 發(fā)送消息 Message 到消息隊(duì)列 MessageQueue 中凝化,消息內(nèi)容一般是 UI 操作。發(fā)送消息一般都是通過 Handler.sendMessage(Message msg)Handler.post(Runnabe r) 兩個(gè)方法來進(jìn)行的酬荞。而入隊(duì)一般是通過 MessageQueue.enqueueeMessage(Message msg,long when) 來處理搓劫。

  3. 消息循環(huán)
    主要分為「消息出隊(duì)」和「消息分發(fā)」兩個(gè)步驟,Looper 會通過循環(huán) 取出 消息隊(duì)列 MessageQueue 里面的消息 Message混巧,并 分發(fā) 到創(chuàng)建該消息的處理者 Handler枪向。如果消息循環(huán)過程中,消息隊(duì)列 MessageQueue 為空隊(duì)列的話咧党,則線程阻塞秘蛔。

  4. 消息處理
    Handler 接收到 Looper 發(fā)來的消息,開始進(jìn)行處理傍衡。

對于 Handler 深员,一些需要注意的地方

  • 1 個(gè)線程 Thread 只能綁定 1個(gè)循環(huán)器 Looper,但可以有多個(gè)處理者 Handler
  • 1 個(gè)循環(huán)器 Looper 可綁定多個(gè)處理者 Handler
  • 1 個(gè)處理者 Handler 只能綁定 1 個(gè) 1 個(gè)循環(huán)器 Looper

常規(guī)情況下蛙埂,這些相關(guān)對象是怎么創(chuàng)建的倦畅?

前面我們說到 Looper 是通過 Looper.prepare()Looper.prepareMainLooer() 創(chuàng)建的,我們不妨看看源碼里面到底做了什么绣的。

我們不得不看看 Looper 的構(gòu)造方法都做了什么滔迈。

顯而易見,確實(shí)在創(chuàng)建了 Looper 對象的時(shí)候被辑,自動創(chuàng)建了消息隊(duì)列對象 MessageQueue燎悍。

Looper.prepareMainLooper() 從名稱也很容易看出來,是直接在主線程內(nèi)創(chuàng)建對象了盼理。而在我們?nèi)粘i_發(fā)中谈山,經(jīng)常都是在主線程使用 Handler,所以導(dǎo)致了很少用到 Looper.prepare() 方法宏怔。

而生成 LooperMessageQueue 對象后奏路,則自動進(jìn)入消息循環(huán):Looper.loop(),我們不妨再看看里面到底做了什么臊诊?

截圖中的代碼比較簡單鸽粉,大家應(yīng)該不難看懂,我們再看看如何通過 MessageQueue.next() 來取消息設(shè)置阻塞狀態(tài)的抓艳。

我們?nèi)∠⒉捎昧艘粋€(gè)無限 for 循環(huán)触机,當(dāng)沒有消息的時(shí)候,則把標(biāo)記位 nextPollTimeOutMillis 設(shè)置為 -1,在進(jìn)行下一次循環(huán)的時(shí)候儡首,通過 nativePollOnce() 直接讓其處于線程阻塞狀態(tài)片任。

再看看我們的消息分發(fā)是怎么處理的,主要看上面的 msg.target.dispatchMessage(msg) 方法蔬胯。

原來 msg.target 返回的是一個(gè) Handler 對象对供,我們直接看看 Handler.dipatchMessage(Message msg) 做了什么。

總結(jié):

  • 在主線程中 Looper 對象自動生成氛濒,無需手動生成产场。而在子線程中,一定要調(diào)用 Looper.prepare() 創(chuàng)建 Looper 對象舞竿。如果在子線程不手動創(chuàng)建京景,則無法生成 Handler 對象。
  • 分發(fā)消息給 Handler 的過程為:根據(jù)出隊(duì)消息的歸屬者炬灭,通過 dispatchMessage(msg) 進(jìn)行分發(fā)醋粟,最終回調(diào)復(fù)寫的 handleMessage(Message msg)
  • 在消息分發(fā) dispatchMessage(msg) 方法中重归,會進(jìn)行 1 次發(fā)送方式判斷:
    1. 若 msg.callback 屬性不為空米愿,則代表使用了 post(Runnable r) 發(fā)送消息,則直接回調(diào) Runnable 對象里面復(fù)寫的 run()鼻吮。
    2. 若 msg.callback 屬性為空育苟,則代表使用了 sendMessage(Message msg) 發(fā)送消息,直接回調(diào)復(fù)寫的 handleMessage(msg)椎木。

常規(guī)的消息 Message 是如何創(chuàng)建的违柏?

我們經(jīng)常會在 Handler 的使用中創(chuàng)建消息對象 Message,創(chuàng)建方式也有兩個(gè) new Message() 或者 Message.obtain()香椎。我們通常都更青睞于 Message.obtain() 這種方式漱竖,因?yàn)檫@樣的方式,可以有效避免重復(fù)創(chuàng)建 Message 對象畜伐。實(shí)際上在代碼中也是顯而易見的馍惹。

Handler 的另外一種使用方式

前面主要講解了 Handler.sendMessage(Message msg) 這種常規(guī)使用方式,實(shí)際上玛界,我們有時(shí)候也會用 Handler.post(Runnable r) 進(jìn)行處理万矾,我們當(dāng)然應(yīng)該看看里面是怎么處理的。

從官方注釋可以看到慎框,這會直接將 Runnable 對象加到消息隊(duì)列中良狈,我們來看看 `getPostMessage(r) 到底做了什么。

我們上面的分析是對的笨枯。在 getPostMessage(Runnable r) 方法中薪丁,我們除了通過 Message.obtain() 方法來創(chuàng)建消息對象外遇西,專門把 Runnable 對象賦值給了 callback,這樣才用了上面做消息分發(fā)的時(shí)候窥突,通過這個(gè)標(biāo)記來判斷是用的 post() 還是 sendMessage() 方式努溃。

到底是如何發(fā)消息的硫嘶?

一直在說通過 sendMessage() 方式來發(fā)消息阻问,到底這個(gè)消息是怎么發(fā)送的呢?

直接看 sendMessageAtTime()沦疾。

enqueueMessage() 里面做了什么称近?

至此,你大概明白了兩種方式的區(qū)別了哮塞。

寫在最后

本次內(nèi)容可能講的比較多和亂刨秆,還望大家跟著到源碼中一步一步分析,最困難的時(shí)候忆畅,就是提升最大的時(shí)候衡未!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市家凯,隨后出現(xiàn)的幾起案子缓醋,更是在濱河造成了極大的恐慌,老刑警劉巖绊诲,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件送粱,死亡現(xiàn)場離奇詭異,居然都是意外死亡掂之,警方通過查閱死者的電腦和手機(jī)抗俄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來世舰,“玉大人动雹,你說我怎么就攤上這事「梗” “怎么了胰蝠?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長裆馒。 經(jīng)常有香客問我姊氓,道長,這世上最難降的妖魔是什么喷好? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任翔横,我火速辦了婚禮,結(jié)果婚禮上梗搅,老公的妹妹穿的比我還像新娘禾唁。我一直安慰自己效览,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布荡短。 她就那樣靜靜地躺著丐枉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掘托。 梳的紋絲不亂的頭發(fā)上瘦锹,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機(jī)與錄音闪盔,去河邊找鬼弯院。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泪掀,可吹牛的內(nèi)容都是我干的听绳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼异赫,長吁一口氣:“原來是場噩夢啊……” “哼椅挣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起塔拳,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤鼠证,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蝙斜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體名惩,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年孕荠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了娩鹉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稚伍,死狀恐怖弯予,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情个曙,我是刑警寧澤锈嫩,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站垦搬,受9級特大地震影響呼寸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猴贰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一对雪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧米绕,春花似錦瑟捣、人聲如沸馋艺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捐祠。三九已至,卻和暖如春桑李,著一層夾襖步出監(jiān)牢的瞬間踱蛀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工芙扎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留星岗,地道東北人填大。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓戒洼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親允华。 傳聞我的和親對象是個(gè)殘疾皇子圈浇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容