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)相关推荐
- android 结束if循环_Android Handler 消息循环机制
前言 一问起Android应用程序的入口,很多人会说是Activity中的onCreate方法,也有人说是ActivityThread中的静态main方法.因为Java虚拟机在运行的时候会自动加载指定 ...
- 通过源码简要分析之Android消息机制Handler、Looper、MessageQueue运行机制
用了许久的Handler,对于Handler的使用确实是比较熟悉,但是具体内部是如何运作的,却只是模糊的.Handler发出的消息怎么到达MessageQueue?MessageQueue的数据怎么被 ...
- Android消息机制Handler用法
这篇文章介绍了Android消息机制Handler用法总结,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 1.简述 Handler消息机制主要包括: Messa ...
- Android消息机制(Handler机制) - 线程的等待和唤醒
我们都知道,Android的Handler机制,会在线程中开启消息循环,不断的从消息队列中取出消息,这个机制保证了主线程能够及时的接收和处理消息. 通常在消息队列中(MessageQueue)中没有消 ...
- Android的消息机制: Message/MessageQueue/Handler/Looper
概览 * Message:消息.消息里面可包含简单数据.Object和Bundle,还可以包含一个Runnable(实际上可看做回调). * MessageQueue:消息队列,供Looper线程消费 ...
- 【Android】Handler 机制 ( Handler | Message | Looper | MessageQueue )
文章目录 I . Handler 机制简介 II . Handler 机制 Handler Message Looper MessageQueue 四组件对应关系 III . Handler ( 消息 ...
- android 消息轮训,Android消息机制Handler,有必要再讲一次
我们在日常开发中,总是不可避免的会用到 Handler,虽说 Handler 机制并不等同于 Android 的消息机制,但 Handler 的消息机制在 Android 开发中早已谙熟于心,非常重要 ...
- Android 消息机制 Handler总结
老久就想着写一篇 关于消息机制的文章来总结一下. Android的消息机制主要是指Handler 的运行机制.我们在开发时有的时候需要在子线程进行耗时的I/o 操作,可能是读取文件或者 访问网络等,有 ...
- android消息机制 Message, Looper,Handler
其原理为:消息线程(默认是主线程,也就是UI线程),维护一个消息循环和一个消息队列,工作线程向消息队列中添加消息,消息循环从队列中取出消息并处理. 其中Looper类负责消息循环和消息队列,Messa ...
最新文章
- Boost:异步操作,需要boost :: asio :: async_compose函数的测试程序
- Haproxy+多台MySQL从服务器(Slave) 实现负载均衡
- 前端用sql 还是mysql_前端小白安装MySQL的踩坑路
- 基于PHPExcel的常用方法总结
- 最新的Jetson TX2刷机细节,以及一些问题(device not managed:选择network layout时的第二种后wifi消失的问题)
- vue如何获取div的宽度_vue获取dom元素高度的方法
- 计算机设计大赛物联网专项赛专栏
- eviews建立时间序列模型_模型建立——时间序列 eviews协整检验(EG两步法(Engle-Granger))...
- 前端页面点击姓名查看详情
- 计算机桌面是快捷方式,我的电脑桌面上的图标都变成快捷方式了怎么处理?
- 架构问题--削峰填谷
- 银行笔试计算机基础知识点归纳,银行笔试:六大行笔试考情及重点梳理(内含免费模考)...
- java短信发送接口开发
- 实践一:mipsel-栈溢出漏洞_开启telnet服务_反弹shell
- [压位DP]Hdu 6149——Valley Numer II
- Multisim-滑动变阻器、稳压二极管
- 你绝对不能错过的7款开源硬件架构
- 顺丰科技2019秋招产品经理(GIS)笔试题选择题部分
- Google的gmail帐户
- 正则表达式匹配字符串中以数字结尾的字符
热门文章
- 离2006年考研还有一个月
- python网络安全宣传周_Python
- 二分类神经网络的特征光谱---2-3至2-9
- java椭圆 类_java 椭圆算法
- 【数理知识】《矩阵论》方保镕老师-第8章-矩阵在数学内外的应用
- 雅客EXCEL(7)-EXCEL居家常用必备函数(vlookup,IF,AND,OR)
- Android Log等级的介绍
- Exynos 4412启动过程(群聊笔记记录)
- 【Sql server: T-Sql 技术内幕 系列】之索引篇
- Gartner表示:2017年全球IT支出上升2.7% 中国IT支出达到2.34万亿