0.前言
最近在重构我的视频播放器,项目有点点复杂,不可能全面的记录
接下来,挑一些要点来记录一下,上下文铺设比较繁琐,所以用伪代码
本文可以算是对Android点将台:烽火狼烟[-Handler-]的补充
功能需求:
如果面板未显示,点击屏幕 [显示面板], 5s后[自动隐藏面板]
如果面板已显示,点击面板[隐藏面板]
1.使用Handler发送延迟消息
可能平时Handler都是post用来切换线程,它的本职是收发消息
这里很容易想到使用Handler发送延迟消息
override fun showPanel() {
isShow = true
//TODO 显示面板处理逻辑
//使用Handler五秒钟之后隐藏面板
mHandler.sendEmptyMessageDelayed(100, 5000)
}
private val mHandler = Handler {
//TODO 显示面板处理逻辑 hidePanel()
false
}
2.问题来了
在用的时候总感觉哪里不对劲,后来想想Handler的模型,应该是上一个消息的锅
我画了一个图,应该很明显,在第四秒点击时,第五秒仍会触发上一次的信息
也就导致了第四秒的显示面板只停留了1s,所以体验很不好,既然发现问题了,那就解决一下呗
override fun showPanel() {
isShow = true
//TODO 显示面板处理逻辑
//使用Handler五秒钟之后隐藏面板
mHandler.removeMessages(100)//移除消息
mHandler.sendEmptyMessageDelayed(100, 5000)
}
解决起来很简单,显示面板时把消息移除就行了
本次记录结束,为了感觉不那么水。瞟一眼源码吧
3.根据what移除消息源码
---->[Handler#removeMessages(int)]-------------------------
|-- 根据消息的what标识删除消息,可见是MessageQueue光环-----------
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
---->[MessageQueue#removeMessages(Handler, int,Object)]-----------
void removeMessages(Handler h, int what, Object object) {
if (h == null) {//Handler为空,直接返回
return;
}
synchronized (this) {
Message p = mMessages;//消息队列的第一个对象
// Remove all messages at front.-----------移除开头符合条件的消息----------
//如果条件为真下面的代码会执行,此时p节点是我们想要移除的
// 貌似只是开头为目标节点才会执行 ---------------
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;//临时变量n 记录下一节点
mMessages = n;
p.recycleUnchecked();//回收掉p消息
p = n;//此时的p是目标的下一节点
}
// Remove all messages after front. ---- 移除开头之后符合条件的消息 --------
while (p != null) {//遍历所有p之后的节点
Message n = p.next; //目标节点的下一节点
if (n != null) {
if (n.target == h && n.what == what//现在n是我们想要移除的
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();//回收掉n消息
p.next = nn;
continue;
}
}
p = n;
}
}
}
4.根据消息的Runnable移除消息
这个和上面差不多,不过很少人用Message的Runnable吧,Message本身可以添加一个Runnable的可执行体
在Handler的dispatchMessage
中会优先触发消息的callback,即Runnable对象,而不会执行Handler自身的回调
---->[Handler#dispatchMessage]---------------
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//如果msg有回调
handleCallback(msg);//处理msg的callback
} else {
if (mCallback != null) {//这个上面举例说明过,不说了
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//回调覆写的handleMessage方法
}
}
---->[Handler#handleCallback]---------------
private static void handleCallback(Message message) {
message.callback.run();
}
---->[MessageQueue#removeMessages(Handler,Runnable,Object)]-----------
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
//注意这里用的是 == ,也就是匹配的是对象的内存地址 ----------
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
---->[Handler#removeCallbacks]---------------
|--- 根据消息的Runnable移除消息
public final void removeCallbacks(Runnable r){
mQueue.removeMessages(this, r, null);
}
所以,也可以根据message的回调移除消息
private val mHandler = Handler()
val callback = Runnable {
//TODO 显示面板处理逻辑 hidePanel()
}
override fun showPanel() {
isShow = true
//TODO 显示面板处理逻辑
//使用Handler五秒钟之后隐藏面板
mHandler.removeCallbacks(callback)
mHandler.sendEmptyMessageDelayed(100, 5000)
}
好了,这篇小记就到这里
点击查看更多内容
1人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦