Handler之消息屏障你应该知道的

本文详细介绍了Android中Handler的同步屏障,包括其插入、删除及使用场景。同步屏障可以阻止普通消息处理,直到异步消息执行完毕,主要用于确保异步消息的优先级。在插入和移除屏障时,特定情况下会唤醒或不唤醒消息队列,影响消息的分发顺序。文章通过实战案例解析了同步屏障如何影响同步和异步消息的处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

由于Handler消息机制的同步屏障问题使用的场景不多,即使不了解可能也不会影响正常开发,因此往往容易被忽视,笔者也是自认比较熟悉Handler的消息机制,在之前同步屏障也算盲区,要想更全面的掌握Handler消息机制,同步屏障就不应该缺席。本文将从同步屏障的插入、删除、使用场景等角度分析同步屏障的工作原理

消息的分类

在这里插入图片描述

Message分为3中:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,同步消息会被挡住,除非撤销屏障。同步屏障后面会重点介绍,这里先了解异步消息。

在Android系统中存在一个VSync消息,它主要负责每16ms更新一次屏幕展示,如果用户同步消息在16ms内没有执行完成,那么VSync消息的更新操作就无法执行在用户看来就出现了掉帧或卡顿的情况,为此Android开发要求每个消息的执行需要限制在16ms之内完成。但是消息队列中可能会包含多个同步消息,假如当前主线程消息队列有10个同步消息,每个同步消息要执行10ms,总共也就需要执行100ms,这段时间内就会有近7帧无法正常刷新展示,应用执行过程中遇到这种情况还是很普遍的。

Android系统设计时自然也会考虑到这种情况,同步消息会导致延迟主要原因在于排队等候,如果消息发送后不必排队等待直接就执行就能够解决消息延迟问题。Android系统中的异步消息就是专门解决消息处理延迟的问题,它需要配合同步屏障(SyncBarrier)一起工作,在发送异步消息的时候向消息队列插入同步屏障对象,消息队列会返回同步屏障的token,此时消息队列中的同步消息都会被暂停处理,优先执行异步消息处理,等异步消息处理完成再通过消息队列移除token对应的同步屏障,消息队列继续之前暂停的同步消息处理

异步消息& 同步屏障 使用场景
ViewRootImpl接收屏幕垂直同步信息事件用于驱动UI测绘
ActivityThread接收AMS的事件驱动生命周期
InputMethodManager分发软键盘输入事件
PhoneWindowManager分发电话页面各种事件

屏障消息

发送同步屏障

同步屏障是通过MessageQueue的postSyncBarrier方法插入到消息队列的。屏障消息(同步屏障有以下特征)

  • ①没有给target赋值,即不用handler分发处理,后续也会根据target是否为空来判断消息是否为消息屏障

  • ②消息队列中可以插入多个消息屏障

  • ③消息屏障也是可以有时间戳的,插入的时候也是按时间排序的,它只会影响它后面的消息,前面的不受影响

  • ④消息队列中插入消息屏障,是不会唤醒线程的(插入同步或异步消息会唤醒消息队列)

  • ⑤插入消息屏障后,会返回一个token,是消息屏障的序列号,后续撤除消息屏障时,需要根据token查找对 应的消息屏障

  • ⑥发送屏障消息的API被隐藏,需要通过反射调用postSyncBarrier方法

MessageQueue#postSyncBarrier

class MessageQueue{
   
	public int postSycnBarrier() {
   
	//currentTimeMills()系统当前时间,即日期时间,可以被系统设置修改,时间值会发生跳变
	//uptimeMills() 自开机后,经过的时间,不包括深度休眠的时间
	//sendMessageDelay,postDelay也都使用了这个时间戳
	//意思是指,发送了这条消息,在这期间如果设备进入休眠状态(如息屏后长时间不操作手机)那么消息是不会被执行的,
	//设备唤醒后到了时间才会执行
	}
	private int postSyncBarrier(long when) {
   
		synchronized (this) {
   
	    	final int token = mNextBarrierToken++;
	        //屏障消息和普通消息的区别是屏障消息没有tartget
	        //也就不会被分发处理(执行)
	        //可以理解为是一个标志位flag
	        final Message msg = Message.obtain();
	        msg.markInUse();
	        msg.when = when;
	        msg.arg1 = token;
	
			Message prev = null;
	        Message p = mMessages;
	        //根据时间顺序将屏障插入到消息链表中适当的位置
	        if (when != 0) {
   
	        	while (p != null && p.when <= when) {
   
	            	prev = p;
	                p = p.next;
	            }
			}
	        if (prev != null) {
    // invariant: p == prev.next
	        	msg.next = p;
	            prev.next = msg;
	        } else {
   
	        	msg.next = p;
	            mMessages = msg;
	        }
	        //返回一个序号,通过这个序号可以撤销屏障
	        return token;
		}
	}

}


移除同步屏障

  • 根据添加屏障时对应的token,在消息队列中找到对应的屏障删除
  • 添加屏障的时候不会唤醒线程,删除屏障时,满足一定条件就唤醒线程
  • 如果屏障在消息队列头部,触发时间到了,不管后面有没有消息,撤除屏障都会唤醒线程。但是如果后面紧跟着
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值