刨根问底——Handler
MessageQueue,顾名思义,就是一个由Message组成的Queue,它(MessageQueue)的内部维护着一条队列,每当我们通过Handler发送一条Message后,这条Message都会被添加到MessageQueue的队列尾部,其实这条消息队列只起到存储消息的作用,并不具备任何循环、处理消息的作用。Looper在这个机制中扮演者循环器的作用,它会不断的从MessageQueue中取到队列头部的Message,然后交给Handler来处理这条Message。至此,思路已经很明显了,Handler发送Message至MessageQueue,同时Looper不断地尝试读取MessageQueue中的Message,发现Message之后将它取出交由Handler来处理,由于Handler的handleMessage是执行在主线程的(假设我们在主线程初始化的Handler),所以此时我们可以在该处执行一些更新UI的操作。
现在我们来将该机制分解成三部分:1.Handler发送Message之后这条Message是怎么进入MessageQueue的;2.Looper是如何循环获取Message的;3.Message是怎么被Handler处理的。
Handler发送Message之后Message是怎么进入到了MessageQueue中的?
说到这个,我们就不得不介绍一下Handler中的sendMessageAtTime这个方法了,这个方法我们平时基本上不会用到,因为我们用的最多的是Handler的sendMessage或者是sendMessageDelayed,又或者是调用Message的sendToTarget方法(handler.obtainMessage().sendToTarget()),那为什么还要介绍这个方法呢?因为我们通过Handler和Message的源码可以看到,无论是调用上述的哪个方法,最后都会走到sendMessageAtTime这个方法中来,这个方法中的实现如下图
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {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);
}复制代码
我们可以看到,执行到这里的时候我们已经追踪到了消息进入队列的入口,我们点进enqueueMessage这个方法中看一下
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}复制代码
我们可以看到,Message是通过这个方法来将消息放入队列的(注意msg.target = this,后面我们会用到),我们再进入MessageQueue中来看一下enqueueMessage这个方法(方法中的一段)
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.next
prev.next = msg;复制代码
在这里,p代表当前消息队列中的队列头部,prev代表p指向的Message的前一条,我们可以看到,在for循环中,prev和p一直从队列头部索引到队列尾部,当跳出for循环时,p指向的Message为null,说明最后一条消息就是prev这个变量所指向的Message,当最后两个表达式执行完毕后,msg被添加到了队列的尾部,到这里Message进入的MessageQueue中的路线就结束了。
.Looper是如何循环获取Message的
前面我们说到,MessageQueue只是Message的容器,它本身不具备选择Message派发给相应Handler的功能,这时就需要Looper来发挥作用了。我们知道Looper被初始化后,都需要调用loop方法,那么这个方法是做什么的?我们来看一下
for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();final long end;try {msg.target.dispatchMessage(msg);end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}复制代码
我们来重点关注一下第2行和倒数第7行的表达式,这里面的queue就是MessageQueue了,我们可以看到,Looper在不断的尝试从MessageQueue中获取队列头部的Message,然后会获取这条Message的target并且执行dispatchMessage()方法,这个target又是个啥?进入我们刚刚看到过的Handler的enqueueMessage方法中,我们可以看到,在该方法的第1行中就是为Message的target赋值,所以target的值是一个Handler,也就是我们发送Message的这个Handler,咦?好像要到最后一步了,Message已经交给Handler了,接下来就是我们最后要说的内容了,Handler是怎么处理这条Message的。
Handler是怎么处理这条Message的?
现在Message已经又交到了Handler的手里,我们来看一下Handler的dispatchMessage方法
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}
}复制代码
我们可以看到这个方法的逻辑非常简单,如果msg被设置了回调方法,就执行它的回调方法,否则就执行Handler里面的相关方法。通常我们并没有为Message设置回调函数,并且都是直接new一个不带回调函数的Handler,所以我们的方法只剩了最后一条执行路线,调用handleMessage方法,这个方法看着好眼熟啊!这不就是我们new Handler的时候实现的那个方法吗?我们点进去看一下
/*** Subclasses must implement this to receive messages.*/
public void handleMessage(Message msg) {
}复制代码
还真是这个方法。从这里开始就是我们熟悉的操作了,在handleMessage中通过what来匹配,然后来执行不同的逻辑,由于我们是在主线程实现的这个方法,所以我们可以在这里面更新相关的UI界面,这样就实现了在子线程中执行操作,在主线程中更新UI的功能了。
到这里我们Handler的消息派发机制大致上已经说完了,由于文笔太烂只说了Handler大致的机制,没有涉及到的知识有很多,比如Looper是怎么存储和获取的、msg的callback是怎么设置的等等。我想只要先掌握了机制的大致流程,以后深入某个功能的时候才不会迷失在海量的代码中。
写这些东西只是想对自己学过的东西做一下笔记,如果对您有了一丝丝的帮助,那就再好不过了。多帮我提提建议啊,文中若有不足,敬请谅解!
转载于:https://juejin.im/post/5a4de624f265da43310e3d3e
刨根问底——Handler相关推荐
- android之handler的刨根问底
对于handler,不管处于哪个层次,这个都是必问的面试题,这也是过来人的一个总结.我换工作还是比较频繁的,曾辗转无锡.苏州.南京.上海,虽然我只工作不到四年,呵呵,信息量好像有点大,但其中问到最多的 ...
- 换个姿势,带着问题看Handler
换个姿势,带着问题看Handler Handler,老生常谈,网上关于它的文章也是"泛滥成灾",但实际开发很少手写Handler, 毕竟,写异步,RxAndroid链式调用 或者 ...
- 为什么UI线程中创建Handler可以不传Looper?
一个APP程序的入口是ActivityThread的main方法,ActivityThread就是我们常说的主线程或UI线程,事实上它并不是一个线程,而是主线程操作的管理者. public stati ...
- Android中Handler消息机制
作用:跨线程通信. 应用:AsyncTask.retrofit都对Handler进行了封装. 四要素:Message.MessageQueue.Looper.Handler Message简介: 线程 ...
- Python:urllib2模块Handler处理器 和 自定义Opener
Handler处理器 和 自定义Opener opener是 urllib2.OpenerDirector 的实例,我们之前一直都在使用的urlopen,它是一个特殊的opener(也就是模块帮我们构 ...
- Android Handler sendMessage和 sendMessageDelayed的使用
sendMessage :立即发送消息 sendMessageDelayed:指定多少毫秒后发送消息. 其实常用的还有2个 sendEmptyMessage 发送空消息, 一般自己想把某种操作放到子 ...
- Handler 实现的一个循环操作
这个使用Handler 实现的一个循环操作还是比较简单的,下面就直接给出demo了 一个间隔5s 弹一次Toast 的demo 如下 public class MainActivity extends ...
- Handler 实现的一个延时操作
今天回顾下handler 记录一个延时的demo 使用 postDelayed 主要代码如下: public class MainActivity extends AppCompatActivity ...
- Android 实现一个验证码倒计时(Handler postDelayed 实现验证码倒计时)
直接看代码吧,很简单 public class MainActivity extends AppCompatActivity {private TextView code;private Handle ...
最新文章
- Docker镜像、容器数据卷和Dockerfile
- cocos2d-x游戏实例(8)-A星算法(4)
- XidianOJ 1146 万神的竞赛
- 程序员接私活的途径以及正确方式。
- TeamTNT通过模仿WatchDog团伙来掩盖其加密劫持足迹
- 驱动层和r3程序通讯的列子参考爱写驱动的女装大佬
- 查看linux是几位的操作系统
- zigbee cc2530 adc转换
- C++ 偏微分数值计算库_一文带你了解计算流体力学CFD及其应用领域
- Angular / RxJs我应该何时退订`Subscription`
- SQL Where子句
- Dataguard之redo传输服务
- 实践两个servlet小项目
- 常用UCI数据集(已处理)
- 用Python手撕一个批量填充数据到excel表格的工具,解放双手!
- 硬件知识学习整理:(上拉,下拉),(三极管),(OC,OD,推挽输出),(NMOS与PMOS),(MOSFET驱动电路),(IR2110S)
- 【读书摘录】《沉默的大多数》(王小波)
- linux下redis设置密码登录(简单易懂)
- 结构建模设计——Solidworks软件之草图绘制中借助新建基准面实现在曲面表面绘制特征的实现步骤总结
- Jetson Nano | darknet (yolov3.4-tiny)摄像头实时检测
热门文章
- 思科光传输功率查询_各品牌网络设备的光功率查看方法(不完全统计)
- day06:02oracle体系结构_存储结构
- 文本框换行_多行文本框的认识以及代码详解
- matlab画扇区,NFCDemo NFC读写测试 ,自动读取每个扇区 块的值 matlab 238万源代码下载- www.pudn.com...
- java环形数组_Java数组模拟环形队列
- easyexcel 设置标题_EasyExcel,让 excel 导入导出更加简单
- html range关联文本框,HTML5gt;meter标签与input(type=range)标签结合制作简易范围指示器...
- hdu1042 java_hdu 1431 素数回文
- js pug 代码_用JS写的windows95操作系统
- Halcon 标定与准确测量