在深入系統的學習Handler的時候,我們接觸到了Looper之所以死循環不會導致CPU使用率過高,是因為使用了Linux下的pipe和epoll機制。
Android的應用層通過Message.java實現隊列,利用管道和epoll機制實現線程狀態的管理,配合起來實現了Android主線程的消息隊列模型。
對Handler,我們在之前整理了如下內容,也上對Handler的機制有了相當程度的了解:
Android Handler 機制(一):Handler 運行機制完整梳理
Android Handler 機制(二):Hander 機制深入探究問題梳理
同時為了更進一步的了解Handler機制,我們整理了Epoll機制的相關基本知識:Linux下Epoll機制概述
本文我們講述一下Android的Handler機制為何使用管道。
一、管道概述
管道,其本質是也是文件,但又和普通的文件會有所不同:管道緩沖區大小一般為1頁,即4K字節。管道分為讀端和寫端,讀端負責從管道拿數據,當數據為空時則阻塞;寫端向管道寫數據,當管道緩存區滿時則阻塞。
在Handler機制中,Looper.loop方法會不斷循環處理Message,其中消息的獲取是通過 Message msg = queue.next(); 方法獲取下一條消息。該方法中會調用nativePollOnce()方法,這便是一個native方法,再通過JNI調用進入Native層,在Native層的代碼中便采用了管道機制。
二、Handler為何使用管道?
我們可能會好奇,既然是同一個進程間的線程通信,為何需要管道呢?
我們知道線程之間內存共享,通過Handler通信,消息池的內容並不需要從一個線程拷貝到另一個線程,因為兩線程可使用的內存時同一個區域,都有權直接訪問,當然也存在線程私有區域ThreadLocal(這里不涉及)。即然不需要拷貝內存,那管道是何作用呢?

Native層:
三、Handler為何采用管道而非Binder?
- 從內存角度:通信過程中Binder還涉及一次內存拷貝,handler機制中的Message根本不需要拷貝,本身就是在同一個內存。Handler需要的僅僅是告訴另一個線程數據有了。
- 從CPU角度,為了Binder通信底層驅動還需要為何一個binder線程池,每次通信涉及binder線程的創建和內存分配等比較浪費CPU資源。
從上面的角度分析可得,Binder用於進程間通信,而Handler消息機制用於同進程的線程間通信,Handler不宜采用Binder。
四、Android 6.0及以后的機制
在Android 6.0及以前的版本使用管道與epoll來完成Looper的休眠與喚醒的。
在Android 6.0及以后的新版本中使用的是eventfd與epoll來完成Looper的休眠與喚醒的。
感興趣的可以進一步的學習和了解管道的知識及eventfd的知識,並比較一下兩種機制的優劣,進而明白Android官方為何對此機制進行調整。