Android消息機制其實指的就是Handler的消息機制。

以上模型的解釋:
1.以Handler的sendMessage方法為例,當發送一個消息後,會將此消息加入消息隊列MessageQueue中。
2.Looper負責去遍歷消息隊列並且將隊列中的消息分發給對應的Handler進行處理。
3.在Handler的handleMessage方法中處理該消息,這就完成了一個消息的發送和處理過程。 這裡從圖中可以看到參與消息處理有四個對象,它們分別是 Handler, Message, MessageQueue,Looper。
ThreadLocal 是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據,數據存儲以後,只有再指定線程中可以獲取到存儲的數據,對於其他線程來說則無法獲取到數據。
我們看下ThreadLocal是如何存儲數據的:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在源碼裡面我們可以看出 ThreadLocal在存儲數據的時候,會先拿到當前線程,然後根據當前線程會拿到一個叫做ThreadLocalMap 的Map數組;
那麼ThreadLocalMap 又是什麼呢?
我們可以看到在CreateMap 裡面是創建了ThreadLocalMap ,並且把我們當前線程當作Key,傳遞過去的 Value就是我們在調用ThreadLocal.set(T)傳過來的值
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocal是如何獲取數據的
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//會先根據當前線程找到對應的ThreadLocalMap,如果沒有就創建
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//如果ThreadLocalMap 就會去創建ThreadLocalMap
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

通過以上代碼我們可以看出ThreadLocal是如何保證數據存儲以後,只有再指定線程中可以獲取到存儲的數據,對於其他線程來說則無法獲取到數據的了。
我們如何保證Acticity的默認線程是主線程的呢
在Acticity 中我們用到的線程是ActivityThread這個線程,在這個線程的
main(String[] args)方法裡面我們可以看到下面代碼
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}

//上面的代碼裡面我們可以看到創建 ActivityThread的 Looper.prepareMainLooper(); Looper.loop(); 保證了 ActivityThread為主線程。
創建全局唯一Looper對象和全局唯一MessageQueue消息對象

Activity中創建Handler


消息發送


消息處理


消息阻塞和延時
Looper 的阻塞主要是靠 MessageQueue 來實現的,在next()@MessageQuese 進行阻塞,在 enqueueMessage()@MessageQueue 進行喚醒。主要依賴 native 層的 Looper 依靠 epoll 機制進行的。
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞和延時,主要是next()中nativePollOnce(ptr, nextPollTimeoutMillis)調用naive方法操作管道
nativePollOnce(ptr, nextPollTimeoutMillis);
}
}

阻塞和延時,主要是next()中nativePollOnce(ptr, nextPollTimeoutMillis)調用naive方法操作管道,由nextPollTimeoutMillis決定是否需要阻塞nextPollTimeoutMillis為0的時候表示不阻塞,為-1的時候表示一直阻塞直到被喚醒,其他時間表示延時。
喚醒
主要是指enqueueMessage()@MessageQueue 進行喚醒。
boolean enqueueMessage(Message msg, long when) {
//在這裡喚醒阻塞的方法
if (needWake) {
nativeWake(mPtr);
}
}

簡單理解阻塞和喚醒 就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作。 這裡採用的epoll機制,是一種IO多路復用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作,本質同步I/O,即讀寫是阻塞的。 所以說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。 從阻塞到喚醒,消息切換

延時入隊

主要指enqueueMessage()消息入列是,上圖代碼對message對象池的重新排序,遵循規則(when從小到大)。 此處for死循環退出情況分兩種 第一種:p==null表示對象池中已經運行到了最後一個,無需再循環。 第二種:碰到下一個消息when小於前一個,立馬推出循環(不管對象池中所有message是否遍歷完),進行重新排序。
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/208790.html