android 结束if循环_Android 消息机制(Handler + MessageQueue + Looper)
Author:CrazyWah
Date:2018.03.26
CopyRight:http://crazywah.com
禁止搬運!!!禁止搬運!!!禁止搬運!!!
Android的消息機制主要由Handler、Looper和MessageQueue相互協助。本文建議有過 Handler 使用經驗的同學食用
|Looper|為線程循環執行消息| |-|-| |Handler|進行消息的發送和處理| |Message|攜帶消息的內容| |MessageQueue|管理消息隊列|
太長不想看總結放前頭系列:
經過幾天的源碼閱讀,我大致地摸清楚了Android的 Handller+Looper+MessageQueue合作的消息機制,可總結為以下這幅流程圖:
最后面還有一個面試被問到的有意思的問題,不看正文也建議去看看。
1、機制簡述
以下控件全部都是在android.os包之下的
1.1、Handler(處理器):開發時最常接觸到的控件
Handler 的一些特點:
- 每個 Handler 對象對應一個創建時所處線程相關聯的循環器(Looper)
- Handler 會將 Message 交付到對應 Looper 上運行
什么時候會用到 Handler 呢?一般在我們需要要跨線程執行動作的時候
怎么用呢? 我們可以通過以下方法來安排 Message 加入到 MessageQueue 隊列中。
- post(Runnable)
- postAtTime(Runnable, long)
- postDelayed(Runnable, Object, long)
- sendEmptyMessage(int)
- sendMessage(Message)
- sendMessageAtTime(Message, long)
- sendMessageDelayed(Message, long)
post 開頭的這些方法是用于指定你自己定義的 Runnable,方法內部幫你把 Runnbale 包裝在 Message 中再加入隊列中的。當消息需要被執行來到 Handler 中的 dispatchMessage() 方法并發現有 runnable 時(Message.callback 字段)會直接執行 runnable。 send 開頭的這些方法是用于將數據封裝到 Bundle 中并綁定在 Message 對象中然后由 Handler 中的 dispatchMessage() 分發,傳入的回調接口的 handleMessage() 方法進行處理。如果回調接口沒有處理會調用 Handler 的 handleMessage() 方法進行處理(當然,你必須先實現 Handler 的這個方法) 注意一點:當 Message 有 Runnable 的時候,handleMessage 是不會被出發的,留意
1.2、Looper(循環器)
Looper 是 Message 的循環器,使其所綁定的線程循環執行 message 中的 Runnable 或執行 Handler 的 callback.handleMessage() 方法或自身 Handler 自身的 handleMessage() 方法。線程是默認沒有消息循環器關聯的,如果想要創建一個線程用作循環器,需要以下步驟: 1. 在創建的線程運行之初調用 Looper.prepare(); 2. 然后調用 Looper.loop(); 方法讓線程開始循環處理消息 3. 若干時間后當不再需要時可以調用 Looper.end(); 結束這個線程的循環器(或線程被終止)。
1.3、Message(消息)
定義一個具有必要的屬性和任意類型的數據的Message對象可以發送至Handler。該對象包括兩個額外的int類型變量和一個Object類型變量在許多情況下可供自定義分配。
雖然Message的構造函數是對外開放的,但是官方建議我們多使用obtain()方法來獲取Message 的對象,以復用久的 Message 對象,一定程度上減輕創建對象帶來的性能開銷。
1.4、MessageQueue(消息隊列)
由 Looper 主動調用、用于管理 Message 隊列的類。Message 經過 Handler 的入隊操作會加入到 Looper 所擁有的 MessageQueue 中。
你可以通過調用 Looper.myQueue() 來獲取當前線程相關聯的 Looper 的 MessageQueue
2、源碼分析
2.1、Looper 源碼分析
2.1.1、Looper 的慣常用法
Demo 代碼
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler() {public void handleMessage(Message msg) {// process incoming messages here}};Looper.loop();}}2.1.2、Looper 的 prepare()源碼
Looper 的構造函數被私有了,唯一能創建 Looper 對象的方法就是調用 prepare() 方法了
/*** 將當前線程初始化為一個 Looper (循環器),而后你可以在當前線程創建一個或多個 Handler 對象來引用這個 Looper。* * 必須在調用Looper.loop()之前先調用Looper.prepare()** 可以調用Looper.end()結束Looper*/public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {//如果當前線程中已經有Looper對象(即已調用過prepare()方法)則拋出異常if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}/*** 初始化當前 Looper 對象:* 1. 創建消息隊列* 2. 綁定當前線程對象*/private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}2.1.3、Looper 的 loop() 源碼
當為線程綁定好 Looper(調用prepare())并創建好 Handler 以后,我們就可以讓 Looper 開始循環執行 Message
/*** 在當前線程中運行消息隊列中的消息*/public static void loop() {//獲取當前線程的Looper對象final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;...//使用死循環來遍歷消息隊列,挑出需要執行的 Message 并分發for (;;) {// 取到一條需要分發的 MessageMessage msg = queue.next();if (msg == null) {return;}...try {//調用 message 所綁定的目標 Handler 的 dispatchMessage(msg) 方法,由 Handler 決定怎么操作msg.target.dispatchMessage(msg);...}...//將已處理完成的 Message 對象重新初始化,等待復用msg.recycleUnchecked();}}2.2、Handler 源碼分析
2.2.1、Handler的慣常用法
2.2.2、Handler構造函數源碼
在使用 Handler 之前我們需要通過 new 獲取 Handler 對象,那么 Handler 的構造函數都做了些什么呢
/*** 該構造函數是默認同步狀態,調用 Handler(Callback callback, boolean async) 創建 Hanlder 對象*/public Handler(Callback callback) {this(callback, false);}/*** 初始化:* 1. 獲取線程中的 Looper 對象* 2. 注入 Handler 中的 CallBack 對象* 3. 初始化是否異步執行的flag** Handler 如果沒有設置為異步的話,默認情況下 Message 的 Runnable 是同步執行的*/public Handler(Callback callback, boolean async) {...//獲取當前線程的線程共享Looper對象mLooper = Looper.myLooper();//如果當前線程共享變量中沒有Looper對象則拋出異常if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}//獲取Looper的消息隊列mQueue = mLooper.mQueue;//綁定當前Handler對象的CallBack接口mCallback = callback;mAsynchronous = async;}2.2.3、Handler的事件分發
/** 處理系統信息的方法 */public void dispatchMessage(Message msg) {//如果Message有callback,則直接運行它的CallBack(即Runnable)對象if (msg.callback != null) {handleCallback(msg);} else {//如果有注入的 CallBack 對象則執行注入的 CallBack 對象的 handleMessage() 方法if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}// 如果注入的 CallBack 攔截了,Handler 的 HandleMessage 方法將不會觸發,反之則會被觸發handleMessage(msg);}}/*** 運行 Message 的 callback*/private static void handleCallback(Message message) {message.callback.run();}2.2.4、Handler 的各種 send 方法
2.2.4.1、sendEmptyMessage(int what)
即時發送空信息至消息隊列
/*** 發送一條僅包含 what 屬性的 Message* * 返回值為 Boolean 值,表示是否發送成功。* 一般情況下,發送失敗是因為當前Looper的消息隊列正在退出*/public final boolean sendEmptyMessage(int what){//當下發送消息return sendEmptyMessageDelayed(what, 0);}2.2.4.2、sendEmptyMessageDelayed(int what, long delayMillis)
延遲發送空信息至消息隊列
/*** 延遲 delayMillis 毫秒后發送僅包含 what 屬性的 Message* 返回值為 Boolean 值,表示是否發送成功。* 一般情況下,發送失敗是因為當前Looper的消息隊列正在退出*/public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {//從全局Message池中獲取復用的Message對象,、//若池中沒有對象可供復用則new一個Message msg = Message.obtain();//賦值what屬性msg.what = what;//調用發送return sendMessageDelayed(msg, delayMillis);}2.2.4.3、sendMessageDelayed(Message msg, long delayMillis)
延遲發送消息至消息隊列
/*** 將消息入隊并排列在目標時間(uptimeMillis)以前的任務之后。* 該信息將會在對應的時間,被綁定好的handler對象中接收并傳入 handleMessage(Message msg) 方法* * 返回值為Boolean值,表示是否發送成功。* 一般情況下,發送失敗是因為當前Looper的消息隊列正在退出*/public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}2.2.4.4、sendMessageAtTime(Message msg, long uptimeMillis)
在指定時間發送指定消息至消息隊列
/*** 將消息入隊并排列在目標時間(uptimeMillis)以前的任務之后。* 該信息將會在對應的時間,被綁定好的handler對象中接收并傳入handleMessage(Message msg)方法* */public boolean sendMessageAtTime(Message msg, long uptimeMillis) {//Looper中的消息隊列MessageQueue queue = mQueue;//如果隊列不存在則拋出異常if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}2.2.4.5、enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
將消息放入消息隊列
/*** 根據Handler的是否異步處理的boolean值來設置Message是否異步處理* 調用MessageQueue的queueMessage(Message msg, long when)方法*/private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}//使用for循環,根據設置好的Message.when找到消息該存放的位置,并插入到隊列中return queue.enqueueMessage(msg, uptimeMillis);}2.2.5、Handler的各種Post方法
2.2.5.1、post(Runnable r)
將一個Runnable即時發布到消息隊列運行
public final boolean post(Runnable r){return sendMessageDelayed(getPostMessage(r), 0);}2.2.5.2、postAtTime(Runnable r, long uptimeMillis)
將一個Runnable按照暨定時間發布到消息隊列運行
public final boolean postAtTime(Runnable r, long uptimeMillis){return sendMessageAtTime(getPostMessage(r), uptimeMillis);}2.2.5.3、postDelayed(Runnable r, long delayMillis)
將一個Runnable延遲delayMillis毫秒后發布至消息隊列運行
public final boolean postDelayed(Runnable r, long delayMillis){return sendMessageDelayed(getPostMessage(r), delayMillis);}2.2.5.4、getPostMessage(Runnable r)
各post方法中用于包裝Runnable成為Message的方法
private static Message getPostMessage(Runnable r) {//從全局Message池中獲取復用的Message對象//若池中沒有對象可供復用則new一個Message m = Message.obtain();m.callback = r;return m;}2.3、Message 源碼分析
2.3.1、Message的結構
我們先大致地看一下Message對象的結構是長什么樣的
public final class Message implements Parcelable{/*** 開發者自定義的消息碼,用于標識消息的相關內容。* 每個Handler都有自己的命名空間,不需擔心會有沖突*/public int what;/** 用于簡單存儲的int值 */public int arg1;/** 用于簡單存儲的int值 */public int arg2;/** 存儲任意對象用于發送給接收者 */public Object obj;.../** 消息的處理時間 *//*package*/ long when;/** 消息附帶的數據 *//*package*/ Bundle data;/** 發送目標Handler對象 *//*package*/ Handler target;/** 本消息的Runnable對象 *//*package*/ Runnable callback;/** 當前Message對象的下一個Message對象 *//*package*/ Message next;/** 用于多線程中對象鎖的對象 */private static final Object sPoolSync = new Object();/** Message 全局對象池 */private static Message sPool;/** Message對象池的大小 */private static int sPoolSize = 0;/** Message對象池的大小上限 */private static final int MAX_POOL_SIZE = 50;/** 當前Message對象是否可復用 */private static boolean gCheckRecycle = true; }通過閱讀 Message 的源碼我們發現,Message 存儲了各種數據: 當 Message 到執行時間后需要被通知的目標 Handler 對象的引用 下一個 Message 對象的引用。從 Message 的結構也能看出來,其實所謂的 Message 隊列并不是隊列結構而是鏈表結構。
為什么使用的是鏈表結構而不是隊列結構,因為鏈表有助于元素的插入和刪除。執行時間的順序由 MessageQueue 的 next 方法執行
2.3.2、Message的對象獲取
雖然 Message 的構造函數是對外開放的,但是官方建議我們多使用 obtain() 方法來獲取 Message 的對象
官方原文:
Constructor (but the preferred way to get a Message is to call Message.obtain())./** 不建議使用 */public Message() {}/*** 嘗試從本地Message池中獲取Message對象* 如果本地池中沒有Message對象則新建一個*/public static Message obtain() {synchronized (sPoolSync) {// 嘗試從本地Message池中獲取Message對象if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}// 如果本地池中沒有Message對象則新建一個return new Message();}/*** 和obtain()一樣是返回一個Message對象* 區別在于,這個方法是拷貝Message參數的值賦予到新的Message對象*/public static Message obtain(Message orig) {Message m = obtain();m.what = orig.what;m.arg1 = orig.arg1;m.arg2 = orig.arg2;m.obj = orig.obj;m.replyTo = orig.replyTo;m.sendingUid = orig.sendingUid;if (orig.data != null) {m.data = new Bundle(orig.data);}m.target = orig.target;m.callback = orig.callback;return m;}/*** 獲取一個指定目標Handler的Message對象*/public static Message obtain(Handler h) {Message m = obtain();m.target = h;return m;}/*** 獲取一個指定目標Handler和可運行callback的Message對象*/public static Message obtain(Handler h, Runnable callback) {Message m = obtain();m.target = h;m.callback = callback;return m;}/*** 獲取一個指定目標Handler和指定運行時間when的Message對象*/public static Message obtain(Handler h, int what) {Message m = obtain();m.target = h;m.what = what;return m;}/*** 獲取一個* 指定目標Handler* 指定內容碼* 綁定任意對象數據* 的Message對象*/public static Message obtain(Handler h, int what, Object obj) {Message m = obtain();m.target = h;m.what = what;m.obj = obj;return m;}/*** 獲取一個* 指定目標Handler* 指定內容碼* 綁定int類型數據arg1* 綁定int類型數據arg2* 的Message對象*/public static Message obtain(Handler h, int what, int arg1, int arg2) {Message m = obtain();m.target = h;m.what = what;m.arg1 = arg1;m.arg2 = arg2;return m;}/*** 獲取一個* 指定目標Handler* 指定內容碼* 綁定int類型數據arg1* 綁定int類型數據arg2* 綁定任意對象數據* 的Message對象*/public static Message obtain(Handler h, int what,int arg1, int arg2, Object obj) {Message m = obtain();m.target = h;m.what = what;m.arg1 = arg1;m.arg2 = arg2;m.obj = obj;return m;}2.4、MessageQueue 源碼分析
MessageQueue 的主要作用是管理 Message 消息的出隊讀取數據與入隊
2.4.1、next()
從 MessageQueue 中讀取消息內容并讓Message出隊
Message next() {...//死循環以從隊列找出有效的 Message 對象// 如果一直沒有 Message,Looper 所在的線程就會一直卡在當前死循環直到有消息到來。for (;;) {...synchronized (this) {// Try to retrieve the next message. Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;...if (msg != null) {if (now < msg.when) {// 當前遍歷到的消息未到執行時間,跳過} else {//當消息到了該執行的時間則將消息從消息隊列拉出并返回// Got a message.mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;...msg.markInUse();return msg;}} else {// No more messages.nextPollTimeoutMillis = -1;}...}...}}2.4.2、enqueueMessage(Message msg, long when)
Message消息的入隊
boolean enqueueMessage(Message msg, long when) {...synchronized (this) { ...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 {//將 Message 消息插入消息隊列的中間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;}3、總結
總結放前面了
如果以上總結有任何錯漏之處非常歡迎各位在issue處提出錯誤處
番外
面試中被面試官問到了一點:如果 Looper 的線程睡了 10 秒,那么本應該在這期間執行的事件會如何執行呢?大家不妨思考一下
.
.
.
.
.
解答:
其實雖然 Message 是一個偽隊列,但是在 next() 的時候 Message 在調用 messgae.next() 以后并不是無腦外拋的,而是做了一次時間比較,看看消息的 msg.when 和當前時間 now 誰更大,然后再外拋的
class MessageQueue{Message next() {...for (;;) {synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;...if (msg != null) {// 這個就是關鍵的時間判斷代碼 <------------!!!!!!!!!!!!!!!!!!!!if (now < msg.when) {// Next message is not ready. Set a timeout to wake up when it is ready.} else {// Got a message....return msg;}} else {// No more messages....}...}...}} }既然知道了 Looper 怎么拿到一個消息,那就好辦了,我們看看消息的 msg.when 怎么來就可以破案了:
class Handler{public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}// 留意這里根據當前時間計算了一次當前 Message 準確的運行時間 <--------------------!!!!return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...// 消息直接以 udateMillis 入消息隊列了 <--------------------!!!!return enqueueMessage(queue, msg, uptimeMillis);} }所以破案了!如果線程睡了十秒鐘,這期間本該執行的 Message 會在線程重新醒來的時候全部執行!
總結
以上是生活随笔為你收集整理的android 结束if循环_Android 消息机制(Handler + MessageQueue + Looper)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: r语言t检验输出检验统计量_数据统计的理
- 下一篇: 烫伤后涂牙膏、蜂蜜、酱油会加重伤情吗?