Android 消息機制深入源碼分析 [ 一 ]
Android 消息機制之 ThreadLocal 深入源碼分析 [ 二 ]
Android 消息機制之 Looper 深入源碼分析 [ 三 ]
Android 消息機制之 Message 與消息對象池的深入源碼分析 [ 四 ]
Android 消息機制之 MessageQueue 深入源碼分析 [ 五 ]
Android 消息機制之初識Handler [ 六 ]
Android 消息機制之 Handler 發(fā)送消息的深入源碼分析 [ 七 ]
Android 消息機制之 MessageQueue.next() 消息取出的深入源碼分析 [ 八 ]
Android 消息機制之消息的其他處理深入源碼分析 [ 九 ]
Android 消息機制總結(jié) [ 十 ]
思考: 如果讓我們來設(shè)計一個操作系統(tǒng), 我們會來怎么設(shè)計 ?
一般操作系統(tǒng)都會有一個消息系統(tǒng), 里面有個死循環(huán), 不斷的輪詢處理其他各種輸入設(shè)備輸入的消息. 比如鍵盤的輸入, 鼠標的移動等. 這些輸入信息最終都會進入操作系統(tǒng), 然后由操作系統(tǒng)的內(nèi)部輪訓機制挨個處理這些信息.
- 設(shè)計一個類, 里面有一個死循環(huán)去做循環(huán)操作.
- 再用一個類來抽象代表各種輸入的信息.
- 這個信息/消息應該還有一個唯一標識符, 用來區(qū)分不同的信息/消息
- 信息/消息中有個對象來保存對應的鍵值對, 方便往信息/消息中存放數(shù)據(jù).
- 信息/消息還需要有字段來表明要執(zhí)行的時間.
- 上面說的這些信息/消息又會組合成一個集合, 常用集合有很多,
ArrayList, LinkedList, Map
. 因為第一點說了使用一個死循環(huán)去處理, 那么這個集合最好是線性和排序較好的. 因為輸入有先后, 一般都是按照輸入的時間先后來構(gòu)成, 既然這樣那就先排除掉Map
, 那么就剩下了ArrayList, LinkedList
. 要知道一個操作系統(tǒng)的事件是非常多的, 也就是說對應的信息/消息很多, 所以這個集合要面臨大量的插入操作, 而在插入效率這塊,LinkedList
具有明顯的優(yōu)勢. 所以這個集合應該是一個鏈表, 但是鏈表又分為多種, 因為是線性排序的, 所以只剩下雙向鏈表與單向鏈表. 又考慮到手機性能的問題, 那就之剩下單向鏈表了, 因為單向鏈表在插入和刪除上的復雜度明顯低于雙向鏈表. - 最后還應該有兩個類, 一個負責生產(chǎn)信息/消息, 一個負責消費這些信息/消息. 因為涉及到消費端, 所以在第二點中, 還應該增加一個字段負責指向消費端.
經(jīng)過這幾點的分析, 是不是發(fā)現(xiàn)其實和 Handler
機制差不多.
-
Looper
負責輪詢 -
Message
代表消息, 內(nèi)部單向鏈表.-
what
作為標識符 -
when
代表時間. -
data
數(shù)據(jù)存放鍵值對 -
target
指向消費端
-
-
MessageQueue
存放消息集合. -
Handler
負責生產(chǎn)和處理消息.
現(xiàn)在將對 Android 消息機制中的這幾個組成部分逐個做出總結(jié).
1. Handler
Handler 是線程間傳遞消息的即時接口, 分為生產(chǎn)線程和消費線程.
生產(chǎn)線程: 在消息隊列中創(chuàng)建, 插入或者移除消息
send *
post *
remove *
消費線程: 處理消息.
handleMessage
每個 Handler 都有一個與之關(guān)聯(lián)的 Looper
和 MessageQueue
, 有兩種創(chuàng)建 Handler 的方式 (這里說的兩種不是兩個構(gòu)造函數(shù), 而是構(gòu)造函數(shù)的分類. )
- 通過默認的構(gòu)造方法, 使用當前線程中關(guān)聯(lián)的
Looper
. - 顯示的指定使用
Looper
.
如果沒有指定 Looper
的 Handler
是無法工作的, 因為它無法將消息放到消息隊列中, 同樣的, 也無法獲取要處理的消息.
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
如果使用的是上面的構(gòu)造函數(shù), 它會檢查當前線程有沒有可用的 Looper
對象, 如果沒有, 就會拋出運行時異常. 如果正常的話, Handler
就會持有 Looper
中的消息隊列對象的引用.
并且, 同一個線程中會有多個 Handler
, 但是他們共享同一個消息隊列, 因為他們共享的是同一個 Looper
對象.
?
2. Message
Message 是容納任意數(shù)據(jù)的容器, 生產(chǎn)線程發(fā)送消息給 Handler
, Handler
將消息加入到 MessageQueue
中等待執(zhí)行. Message 提供了 3 種額外的信息, 以供 Handler
和 MessageQueue
處理時使用.
-
what
: 一種標識符,Handler
使用它來區(qū)分不同的消息, 從而采用不同的處理方法. -
when
: 告訴消息隊列何時處理. -
targe
: 表示是哪一個Handler
應該處理這個消息.
?
Message 一般是通過以下幾種方式來創(chuàng)建的.
public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
Message 通過從消息對象池中獲取得到, 方法中提供的參數(shù)會放到消息體對應的字段中. Handler
同樣可以設(shè)置消息的目標為其自身, 這允許我們進行鏈式調(diào)用. 例如
mHandler.obtainMessage(MSG_SHOW_IMAGE, mBitmap).sendToTarget();
消息對象池是一個消息對象的單向鏈表集合, 它最大長度是 50, 在 Handler
處理完一條消息后, 這條消息就會返回到消息對象池中, 并且重置其所有字段.
當使用 Handler
的 post
方法來執(zhí)行一個 Runnable
的時候, Handler
就會隱式的為我們創(chuàng)建了一個新的消息, 并且設(shè)置 Callback
參數(shù)來存儲這個 Runnable
.
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
?
3. MessageQueue
MessageQueue 是一個消息體對象的無界的單向鏈表集合, 它按照時序?qū)⑾⒉迦腙犃? 最小時間戳的消息將會被優(yōu)先處理.
MessageQueue 通過 SystemClock.uptimeMillis()
獲取系統(tǒng)時間, 維護一個阻塞閾值, 當一個消息體的時間戳低于這個值的時候, 消息就會分發(fā)給 Handler
進行處理.
從 MessageQueue 中調(diào)用 next
方法循環(huán)獲取消息的時候, 如果消息為 Barrier
障柵的時候, 該隊列中的同步消息全部都會被攔截掉, 而放行所有的異步消息.
從 MessageQueue 中調(diào)用 next
方法循環(huán)獲取消息的時候, 在第一次循環(huán), 并且沒有消息或者頭部消息的執(zhí)行時間未到的情況下, 會執(zhí)行 IdleHandler
. 直到下次調(diào)用 MessageQueue.next()
方法. (MessageQueue.next()
方法在 Looper.loop()
死循環(huán)中被調(diào)用.)
MessageQueue 中的 mPendingIdleHandlers
數(shù)組, 初始化的時候, 最小長度為 4.
沒有消息或者消息未到執(zhí)行時間, MessageQueue 將會調(diào)用 nativePollOnce
方法進行阻塞, 有新的消息入隊會根據(jù)情況喚醒. 內(nèi)部的消息執(zhí)行時間到了, 也會自動喚醒.
?
4. Looper
Looper 在 loop()
方法的死循環(huán)中調(diào)用 MessageQueue.next()
方法獲取消息, 然后分發(fā)給 Handler
處理. 一旦消息超過阻塞閾, 那么 Looper 就會在下一輪循環(huán)中讀取到它. Looper 在沒有消息分發(fā)的時候會進行阻塞狀態(tài)(其實是 MessageQueue
進行阻塞.), 當有消息時, 會繼續(xù)輪詢.
每個線程只能關(guān)聯(lián)一個 Looper, 給線程附加另外的 Looper 會拋出運行時異常. 通過使用 Looper 的 ThreadLocal
對象可以保證每個線程只關(guān)聯(lián)一個 Looper 對象.
調(diào)用 Looper.quit() 方法會立即終止 Looper 的死循環(huán). 并移除消息隊列中所有的消息.(延遲消息與非延遲消息).
調(diào)用 Looper.quitSafely() 方法將消息隊列中所有延遲消息移除, 非延遲消息則派發(fā)出去讓 Handler
處理.
只有消息隊列正在關(guān)閉退出的情況下, 在 Looper.loop() 方法死循環(huán)中調(diào)用 MessageQueue.next()
才會返回 null
.
?
至此 Handler 消息機制已經(jīng)分析完畢. 另外還將會有一篇番外的 HandlerThread.