Handler通訊 原始碼分析

2021-08-15 04:33:40 字數 3502 閱讀 4055

1. messagequeue 訊息佇列

執行緒中更新 ui 的時候經常是呼叫 sendmessage() 和 sendmessagedelayed() 這樣 ,我跟蹤**進入到 handler 的 sendmessage() 方法:

public final boolean sendmessage(message msg)

public final boolean sendmessagedelayed(message msg, long delaymillis)

// systemclock.uptimemillis() + delaymillis 當前時間加上延遲時間

return sendmessageattime(msg, systemclock.uptimemillis() + delaymillis);

}public boolean sendmessageattime(message msg, long uptimemillis)

return enqueuemessage(queue, msg, uptimemillis);

}private boolean enqueuemessage(messagequeue queue, message msg, long uptimemillis)

return queue.enqueuemessage(msg, uptimemillis);

}

最後我們發現呼叫的是 messagequeue 的 enqueuemessage() 方法:

boolean enqueuemessage(message msg, long when) 

// 有沒有在使用

if (msg.isinuse())

// 對當前訊息佇列加鎖。

synchronized (this)

// 標記訊息正在使用中

msg.markinuse();

msg.when = when;

message p = mmessages;

boolean needwake;

// 第一次新增資料到佇列中,或者當前 msg 的時間小於 mmessages 的時間

if (p == null || when == 0 || when < p.when) else

if (needwake && p.isasynchronous())

}// 把當前 msg 插入到列表中

msg.next = p; // invariant: p == prev.next

prev.next = msg;

}// we can assume mptr != 0 because mquitting is false.

if (needwake)

}return true;

}

上面的這些**,我們始終沒有看到 handler 呼叫 handlemessage() 方法,但是有了乙個重要的結論,我們每傳送乙個訊息都被儲存到了 messagequeue 訊息佇列中,訊息佇列中採用的是單鏈表的方式。也就是說我如果用下面這段**傳送訊息,在 messagequeue 中應該是如圖所示。

// 傳送 message1

message message1 = new message();

mhandler.sendmessagedelayed(message1, 500);

// 傳送 message2

message message2 = new message();

mhandler.sendmessage(message2);

// 傳送 message3

2. loop 訊息迴圈

我們始終沒有看到 handler 呼叫 handlemessage() 方法,到底什麼時候會執行這個方法。待會解釋,先看一種現象,有時候我們像下面這麼寫會報錯:

必須要下面這樣寫才正常:

new thread()

}.start();

我們在 activity 中從來都沒有呼叫過 looper.prepare(); 這行**,為什麼就從來不報錯呢?我想你可能要去了解一下應用的啟動流程,或者看下我之前寫的 android外掛程式化架構 - activity的啟動流程分析 ,這裡我直接把 main 方法的**貼出來:

public static void main(string args) 

looper.loop();

throw new runtimeexception("main thread loop unexpectedly exited");

}

其實不是不報錯,而是在 activitythread 的 main() 方法中系統早就幫我們寫好了,接下來我們豈不是只需要知道 looper.preparemainlooper() 和 looper.loop() 這兩行原始碼豈不就好了。先來看下 looper.preparemainlooper():

public static void preparemainlooper() 

smainlooper = mylooper();

}}private static void prepare(boolean quitallowed)

sthreadlocal.set(new looper(quitallowed));

}public static @nullable looper mylooper()

這些**相對就比較簡單了,主要還是 threadlocal 的 set 方法,用來保證乙個執行緒只有乙個 looper 物件,這樣就保證了執行緒的安全。接下來看一下 looper.loop() 這行:

整體目錄.png

子執行緒更新ui.png

linux socket通訊原始碼

初學socket通訊,參考的是linuxc程式設計大全的23章的23 5例子,但是發現這個例子原始碼裡有好幾處錯誤,因為初學,很多不懂,吃了虧,因此將修改後能正常執行的 記錄在這裡 參考 server.c include include include include include include...

ucos II 任務間通訊原始碼分析

ucos ii 2.0版本的任務間通訊提供訊息郵箱和訊息佇列兩種機制,都基於核心的事件控制塊機制實現。訊息郵箱 訊息郵箱主要函式分析 訊息佇列 訊息佇列全域性變數 typedef struct os q os q typedef struct os q data os ext os q osqfre...

handler原始碼分析

昨天研究了一下handler的原始碼,今天總結一下 android只有乙個執行緒可以操作ui介面,我們稱之為ui執行緒。每個ui執行緒都維護乙個looper,這個looper中有乙個messagequeue來儲存ui乙個訊息佇列。通過控制這個訊息佇列來實現對ui介面的順序重新整理。handler.s...