關于handler消息處理機制,只要一提到,相信作為一個android工程師,腦海就會有這么一個流程
大家都滾瓜爛熟了,但別人問到幾個問題,很多人還是栽到這個“爛”上面,比如:
- 一個線程是如何對應一個Lopper的?
- messageQueue是如何做到線程安全的?
首先先Looper看一段代碼:
private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
這里先通過sThreadLocal.get()去獲取這個Looper, 獲取不到再去通過new (Looper(quitAllowed))去創建Looper()對象。再來看看這個構造函數:
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
構造函數私有的,只能通過Prepare去初始化,這里就形成了一個關系,一個Looper 對應一個MessageQueue。
回過頭再來分析,線程是如何和Looper對應的。
/*** Return the Looper object associated with the current thread. Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}
在Prepare()的時候給sThreadLocal.set(Looper), 獲取Looper的對象是通過sThreadLocal.get()返回的,下面看看sThreadLocal:
// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這是一個靜態成員變量所有的Looper對象共有屬性。
sTreadLocal保存線程的唯一值,這樣就保證一個線程對應一個looper.
至于MessageQueue的線程同步,會有奇葩的人問到,無非就是syncnized關鍵字,或者加鎖。
boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}
這里用的是一個加鎖的方式。
在Loop.loop中有一死循環,那么當沒有消息時,主線程不可能死掉吧?
“`
在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作。這里采用的epoll機制,是一種IO多路復用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作,本質同步I/O,即讀寫是阻塞的。 所以說,主線程大多數時候都是處于休眠狀態,并不會消耗大量CPU資源。
還有什么關于handler的問題,希望大家可以提出來。