Android 系統(tǒng)用 Binder 機(jī)制進(jìn)行進(jìn)程通信,用 Handler 進(jìn)行線程通信,本文從 Message 的生命流程看懂 Handler 框架。

1、什么是 Handler 框架?
Handler 框架是 Android 用于線程通信的一種機(jī)制,采用消息隊列的形式。主要包括消息隊列(MessageQueue)、消息發(fā)送和處理器(Handler)、消息驅(qū)動器(Looper) 以及消息載體(Message)。
2、Handler 是怎樣的工作流程?
通過 Handler 將一個 Message 發(fā)送給 MessageQueue,Looper 會不斷的從 MessageQueue 中取,然后發(fā)送給相關(guān)聯(lián)的 Handler 去處理。如上圖。
1)Message 的獲取是比較自由,可以自己 new,也可以 Message.obtain。Message 內(nèi)部有一個 static pool,用于重用消息。
2,3)發(fā)送消息的方式非常多,最終都會通過 Handler.enqueueMessage 方法將消息添加到消息隊列中。在這個過程中 Handler 會為 Message 設(shè)置執(zhí)行時間(when)、處理器(target,就是當(dāng)前 Handler) 兩個關(guān)鍵屬性。
4)MessageQueue 會根據(jù) Message.when 在內(nèi)部隊列的排序,將 Message 插在一個合適的位置。MessageQueue 內(nèi)部是使用單鏈表的形式實現(xiàn)的隊列,便于插入和刪除,Message.next 屬性。
5,6,7)Looper 是一個驅(qū)動器,loop 方法會死循環(huán)調(diào)用 MessageQueue.next 方法,從消息隊列中取消息,然后分發(fā)到消息的處理器(Message.targe)。
8,9)至此,這一次通信就完成了,消息會被回收進(jìn)入池子。
3、延時執(zhí)行是如何實現(xiàn)的?
1)Message 有一個 when 屬性記錄消息執(zhí)行的時間;
2)MessageQueue.next 在取消息的時候,如果發(fā)現(xiàn)第一個消息的執(zhí)行時間還沒到或者沒有消息可執(zhí)行,就會阻塞,調(diào)用 nativePollOnce 進(jìn)行休眠,這里會傳遞一個休眠時間。休眠結(jié)束或者有新的消息來到,都會結(jié)束休眠,繼續(xù)取消息。
3)設(shè)置 when 屬性和 next 方法比較時間的時候,使用的是 SystemClock,這個類記錄系統(tǒng)從啟動到現(xiàn)在的時間,他不受用戶修改時間的影響。
4、線程通信是如何實現(xiàn)的?
每一個線程只會有一個 MessageQueue 和 Looper,他們可以關(guān)聯(lián)非常多的 Handler。你可以在線程 A 通過關(guān)聯(lián)線程 B 的 Handler 發(fā)送消息,即可完成通信。Handler 有一個構(gòu)造方法可以指定 Looper,你只需創(chuàng)建 Handler 的時候指定想要通信線程的 Looper 即可。默認(rèn)情況下,會使用當(dāng)前線程關(guān)聯(lián)的 Looper。
這里就要保證每個線程只有一個 Looper,其內(nèi)部是使用 ThreadLocal 實現(xiàn)。Looper.prepare 方法會先檢查當(dāng)前線程是否已經(jīng)有了 Looper(通過 ThreadLocal.get),如果沒有,創(chuàng)建一個,并保存到 ThreadLocal 中。
5、子線程如何使用 Handler 框架?
Handler 能夠工作,主要是當(dāng)前線程的 Looper 及 MessageQueue 準(zhǔn)備好了,所以子線程如果想使用 Handler,調(diào)用 Looper.prepare 和 Looper.loop 方法,讓當(dāng)前線程 Looper 和 MessageQueue 工作就好。
6、Android 主線程為什么沒有因為 loop 死循環(huán)卡死?
謝謝?@Gityuan 老師?的答案。
其實死循環(huán)并不會卡死線程,這是屬于正常執(zhí)行的一部分。所謂“卡死”其實是指 UI 卡頓,造成這個問題其實是因為 Handler 執(zhí)行的某個任務(wù)(Message)時間太長,導(dǎo)致后面更新 UI 的任務(wù)被阻塞了。這也是為什么不在主線程執(zhí)行耗時操作的原因。
由此引出的一個問題是為什么只能在主線程更新 UI?根本原因是 View 操作是非線程安全的,如果多線程就會造成 UI 錯亂。所以解決線程安全有兩個方向,一是將 View 寫成線程安全,二是在一個線程里執(zhí)行。
7、其他
1)消息隊列主要使用了生產(chǎn)者消費(fèi)者的設(shè)計模式,因此消息隊列(MessageQueue)保證了線程安全。
2)附上一篇非常好的 Handler 源碼解析文章。
感謝?@Ruheng 老師?的博客。