为了账号安全,请及时绑定邮箱和手机立即绑定

[- Video篇 -]:记一次Handler的使用

标签:
Python
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人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消