日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android 结束if循环_Android 消息机制(Handler + MessageQueue + Looper)

發布時間:2025/4/5 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 的慣常用法

  • 創建一個裝載 Looper 的線程
  • 在需要被制作為消息循環器的線程開始時調用 Looper.prepare(); 為線程創建 Looper 對象
  • 在所有初始化完成后調用 Looper.loop(); 開始循環執行消息隊列。
  • 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的慣常用法

  • 在需要使用到 Handler 的線程中新建一個 Handler(在 Activity 的生命周期內創建的 Handler 將綁定在 UI 線程的 Looper 上)
  • 定義并傳入 CallBack 對象,用于處理分發回來的 Message
  • 在需要通知線程進行操作的時候調用 Handler 的 send 方法或 post 方法。(若是send類型的方法將會調用CallBack的handlerMessage(Message msg)、若是post類型的方法將會調用post時傳遞的Runnable對象中的run()方法)
  • private Hanlder handler = new Handler(new Handler.CallBack(){@Overridepublic boolean handleMessage(Message msg) {//do something when get a messagereturn false;}});//send message or post runnable when you want to notify the handler to do somethinghandler.sendEmptyMessage(0);

    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)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。