从源码分析Handler的postDelayed为什么可以延时?
来源:互联网 发布:mpv播放器 mac下载 编辑:程序博客网 时间:2024/09/21 09:25
昨天一个朋友去面试,回来说面试官问了他一个有意思的问题,然后被面试官各种调侃。。
什么问题呢?中国人都能看得懂的一个问句:Handler的postDelayed为什么可以延时???
握草~我只知道Handler有个延时的方法叫postDelay,为啥延时我哪知道哇!!!哈哈,相信很多朋友都是有这种感受的。今天单独开篇博客来说这个问题,也表达了我对Handler的敬重之情吧,不废话了,开车~~
我们由简入深,步步击破,先来看看用法:
提交一个任务到MessageQueue,Handler提供了上面的5种方式。相信大家看到方法即知意。今天的主角也正好是最后一个。一般我们都会使用Handler直接post一个延时任务:
第一个参数就是要延时执行的任务,第二个参数就是延时时间(毫秒级)。
执行成功则返回true,并将任务添加到消息队列中(MessageQueue)。
注意:返回true不代表任务执行成功,只是成功提交到任务队列。如果Looper提前在任务延时时间前退出,那么该任务将会被删除。
提交任务失败返回false,原因一般都是Looper处理消息队列退出。
上面的代码很简单,相信大家都能看懂。那么如何实现延时的呢?走,去Handler家里瞧瞧~
1、进入postDelayed方法:
可以看到postDelayed方法实际上调用了Handler的sendMessageDelayed方法,然后调用getPostMessage方法对任务进行封装处理。
getPostMessage方法中创建了Message对象,并将任务给了callback。
2、进入sendMessageDelayed方法:
在sendMessageDelayed方法中:
(1)第一步首先对delayMillis(延时时长)进行判断,如果是负值,则将delayMills置为0,从这也能看出来,如果我们传入的延时时间小于0的话,调用postDelayed和调用post方法是相同的。
(2)第二步调用了sendMessageAtTime方法,将message传入,并在当前时间加上了延时时间 delaymillis(当前时间 + 延时时间),由此可以看到,此时变成了绝对时间,即到了绝对的时间,此任务应被处理。
3、进入sendMessageAtTime方法:
在sendMessageAtTime方法中:
(1)首先将Handler的mQueue初始化给queue,然后对queue进行null的判断,如果为null,即抛出sendMessageAtTiem() called with no mQueue异常。这块也很容易理解,我们都知道Handler的运转离不开Looper的存在,在主线程中使用Handler,我们没有创建Looper是因为系统帮我们创建了,在Looper的创建同时,MessageQueue也会被相应创建,从源码我们也能看到:
所以,如果mQueue为null的话,即证明Handler没有绑定Looper,ok,异常的原因我们解释明白了。
(2)判断结束后,调用了enqueueMessage方法,将消息队列、任务消息、绝对的执行时间作为参数传入。
4、进入enqueueMessage方法:
在enqueueMessage方法中,调用了Looper中创建的MessageQueue实例的enqueueMessage方法,将任务和绝对时间作为参数传递。
5、进入MessageQueue的enqueueMessage方法:
方法很长,在synchronized方法中的我们来看核心的代码:
(1)在if判断中:我们看到when==0或者when < p.when的情况下会执行if语句,即我们传入的绝对时间等于0或者绝对时间还没有到,那么该任务将会阻塞当前任务队列。此时我们看变量needWake被赋值为mBlocked.在next()方法内部,如果有阻塞(没有消息了或者只有Delay的消息),会把mBlocked这个变量标记为true:
所以,此时needWake为true。从字面含义很容易理解,该标志为是否需要唤醒。(need---wake)
然后看enqueueMessage方法的末尾:
可以看到needWake此时为true,将调用native层的nativeWake方法,就是将MessageQueue唤醒。
怎么理解呢?
(1)前面由于我们提交了延时任务,由于时间未到,导致了当前MessageQueue被阻塞,needWake为true。
(2)此时如果有新的任务被提交到MessageQueue,此时由于needWake为true,nativeWake方法被调用,新的任务被插入到队列头部,即延时任务的前面,MessageQueue.next方法被唤醒。
(3)此时next取到刚刚新加的任务,如果没有延时会立刻交给Looper去处理。Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表。
(4)如果延时任务还没到时间,计算一下剩余时间继续阻塞;直到阻塞时间到(即延时时间到)或者下一次有Message进队,此时else的方法将被处理,即处理当前的任务,此时整个流程就结束了。
方法链为:
postDelayed -----> sendMessageDelayed -----> sendMessageAtTime -----> enqueueMessage (此时Handler中的方法调用结束)-----> MessageQueue的enqueueMessage
现在延时的流程是不是就比较清晰了:
- postDelay()一个1秒钟的MyTask任务、消息进队,MessageQueue开始阻塞,Looper阻塞,mBlocked为true,在enqueueMessage的if中将needWake = mBlocked。
- 然后post一个新的任务、消息进队,判断现在A时间还没到、正在阻塞,把新的任务插入消息队列的头部(MyTask任务的前面),然后此时needWake为true调用nativeWake()方法唤醒线程。
- MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;
- Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续阻塞;
- 直到阻塞时间到或者下一次有Message进队;
ok,上面我们结合源码来说明了Handler执行延时任务的原理,相信大家也有了更深的认识。本篇博客内容就这些啦,thks~
参考:http://www.tuicool.com/articles/nqeIVj
- 从源码分析Handler的postDelayed为什么可以延时?
- 从源码角度分析postDelayed原理
- postDelayed源码分析-[Android_YangKe]
- 从源码的角度分析Handler机
- Handler的源码分析
- Handler的源码分析
- handler的源码分析
- 从源码分析Android中Handler的消息传递机制
- AS中用handler.postdelayed的问题
- handler的sendMessage、removeMessages、post、PostDelayed等
- Handler机制-从源码角度分析
- [Android] 从源码分析 Handler 消息机制
- 菜鸟从源码分析Handler消息机制
- Handler消息处理机制---从源码分析
- Android 从源码分析Handler消息机制
- 延时操作postDelayed
- handler机制的源码分析
- 关于 android 中 postDelayed方法的讲解,作用:延时调用。
- 揭秘之从RecyclerView滑动监听到Gilde平滑加载图片
- 如何使用 Grape-Swagger 生成 API 文档
- Eclipse去除js(JavaScript)验证错误
- 各种排序算法的Python实现
- PHP的对象和引用(写时复制)
- 从源码分析Handler的postDelayed为什么可以延时?
- 通俗理解P和NP
- kingshard架构设计和功能实现
- String 对象中的 正则表达式方法 match
- hive使用beeline测试JDBC连接
- Android——Hook(钩子函数)动态注入代码
- Jedis
- linux命令学习-文件目录之rm命令
- 关于constructor 属性查看对象是否为数组详情