注:如下內容基於Android API Version 27(Android 8.1)Linux Kernel 3.18.0linux
Handler
、Looper
和MessageQueue
組成了Android的消息循環系統。消息循環系統是Android App的神經中樞,不管是與AMS/WMS
打交道,仍是UI繪製,亦或是手機輸入事件的派發都依賴於消息循環系統。android
Android的消息循環運行於底層,咱們在上層開發遇到的一系列的組件生命週期回調,好比Activity
的onCreate
和View
的onTouchEvent
,都是從主線程的消息循環(Looper.loop
通過層層調用過來的。咱們在這些生命週期裏填寫代碼最終造成了可使用的App,而這一切都是被底層的消息循環驅動着,所以咱們能夠說Android App是一種基於消息驅動模型的應用程序。git
消息隊列,用於存儲和獲取消息,包括Java層消息、Native層消息和各類文件描述符(FD)就緒消息。github
消息循環,內部包含一個MessageQueue
對象,經過一個無限循環的loop方法,持續從MessageQueue
獲取並處理消息。socket
Java層幫助類,內部包含一個Looper
對象,Handler
經過Looper
中的MessageQueue
往Java層的消息隊列發送消息。Handler
同時提供了消息處理的回調函數,Handler
收到消息回調後再決定將消息派發到哪裏去。函數
消息循環起始於Looper
的loop
方法調用,此方法內部是一個無限的for
循環,線程一旦進入loop
就再也出不去了,除非主動退出。之後線程所作的事就三件:取消息、執行消息處理函數和等待,而線程的全部有意義的工做都發生在消息處理函數中。oop
Android的消息循環同時兼顧了Java層和Native層,Jave層和Native各自維護者本身的消息隊列,Native層同時還監控着一批FD
,FD
就緒也是做爲一種消息進行處理的。消息循環首先嚐試消費Native層的消息(包括普通消息和FD
消息),消費完後再消費Java層的消息,若是Java層沒有可供消費的消息了,線程就會在下一次消費Native時層阻塞在Native的epoll_wait
調用上,epoll_wait
用於等待其所監控的FD
就緒,若是Java層發送了新消息或者Native層發送了新消息,再或者有FD
就緒了,線程就會被喚醒,從而消息循環就會繼續處理消息,直到Java、FD
和Native都沒有消息了線程就又再次陷入阻塞。 Java層的消息在Java層處理,Native層的消息在Native層處理,Java層的消息和Native層的消息的惟一聯繫是他們在同一個循環線程中處理,線程阻塞是發生在Native層,線程喚起是發生在Java層和Native層,線程阻塞的時長和當前是否有消息或者下一個消息的處理時間有關。佈局
Java應用程序經過Handler
將消息發送給MessageQueue
,消息按照時間從先到後進行排序放入鏈表中,若是消息要當即被處理,也就是說沒有延遲的消息,此時若是線程處於阻塞狀態,Java層經過jni
調用Native層的接口喚醒線程,線程喚醒後會從Nativie的Looer.pollOnce
返回到Java層的nativePollOnce
,而後繼續處理Java層的消息,消息處理完後根據是否還有消息或者下一個消息等待處理的延遲時間來決定Native層下次是一直阻塞仍是使用一個超時時間進行阻塞。大體流程就是:阻塞-發消息-喚醒線程-處理消息-阻塞。post
Java層的MessageQueue
對象持有一個Native層的MessageQueue
對象,Native層的MessageQueue
持有一個Native層的Looper
對象,Java層的MessageQueue
調用Native的nativePollOnce
最終調到Native的Lopper.pollOnce
。Native的Lopper
建立時會將本身加入到Native的線程本地存儲中去(TLS
),以便之後在線程執行的任意地方拿到Looper
對象向Native消息隊列發送消息。.net
Native層的Looper
使用eventfd進行線程的喚醒,eventfd
是linux系統調用,經過eventfd
建立一個FD
,而後將這個FD
和其餘真正要監控的FD
加入到同一個epoll
監控列表中,當須要喚起線程時向這個FD
寫入一個字符,這樣epoll
檢測到有FD
就緒就會從阻塞中喚醒。當Java層須要喚醒Native層的epoll
阻塞時只須要調用Native層的方法向event FD寫入一個字符,這樣epoll_wait
就返回了。
Native層的Lopper.pollOnce
從epoll_wait
返回後首先處理Native消息隊列中的消息,Native消息隊列的消息是經過Native Lopper.sendMessage
函數添加的,每個消息都綁定了一個MessageHandler
對象,處理消息就是調用MessageHandler
的handleMessage
函數將消息對象自己傳給發送消息者。處理完普通消息後,Native Looper
接着處理FD
就緒的消息,每個FD
消息在添加的時候(Looper.addFD
)會關聯一個LooperCallback
對象,Native Looper
就是經過回調FD
所綁定的LopperCallback
來由使用方本身處理FD
就緒的消息的。 處理完全部的這兩種消息後Native Looper
就返回了,接着線程就走到了Java層的消息處理邏輯中去了。
咱們之因此在建立了Looper
的線程中的任意位置能夠訪問這個Looper
緣由是Looper
對象是藉助ThreadLocal
存儲的。ThreadLocal
即線程局部存儲。Looper
內部聲明瞭一個靜態的ThreadLocal
對象,TheadLocal
內部是以線程對象爲key,線程局部數據爲value存儲在一個類map的結構中的。只要線程調用Looper.prepare
將當前建立的Looper
對象存在ThreadLocal
中,之後在線程執行的任意位置均可以調用Looper.myLooper
將存儲的Looper
對象拿出來,而且每一個線程對應各自的Looper
對象。
關於線程局部存儲能夠參閱:Android 線程局部存儲ThreadLocal原理總結
AMS
和WMS
對App進程的回調從binder線程轉到App主線程。vsync
信號的處理。ViewRootImpl
刷新佈局基於vsync
信號,而vsync
信號是經過讀取socketpair的FD
得到的,將socketpair
的FD
加入到Native的消息輪詢中能夠直接使vsync
的回調發生在主線程,從而避免了一次線程切換。FD
加入到Native的消息輪詢中從而實現了消息直接於主線程處理。參考
blog.csdn.net/luoshengyan…
androidxref.com/8.1.0_r33/x…
androidxref.com/8.1.0_r33/x…
linux.die.net/man/2/event…
linux.die.net/man/2/socke…
chao-tic.github.io/blog/2018/1…