消息传递和同步屏障机制全面解析

一、消息机制原理

Handler消息机制老生常谈了,必备八股之一。但是每次看都有新收获,故好好总结一下Handler相关知识。

1.1 基本概念

1、Handler

用于发送和处理消息的类,有多种重载的构造方法,通过一系列sendXXXpostXXX方法来发送消息到消息队列,然后通过实现Handler.Callback接口或重写handleMessage方法处理消息

2、MessageQueue

消息队列,它是一个链表结构,用以存放handler发送的消息,实现了获取消息的方法next()和移除消息及消息处理回调的方法(removeXXX系列方法)

3、Message

消息,承载一些基本数据,消息队列存放对象。维护了一个消息对象池,可以复用消息,避免创建太多消息对象占用过多内存,导致APP卡顿。

消息分类:
在这里插入图片描述

4、Looper

消息机制的灵魂,用以不断调度消息对象并且分发给handler处理。Looper是同线程绑定的,不同线程的Looper不一样,通过ThreadLocal实现线程隔离。

1.2 消息机制主流程

1、发送消息

在这里插入图片描述

可以使用sendMessage(以及一系列 sendXXX的消息发送方法)和post方法发送即时同步消息,或通过sendXXXDelayed和postDelayed发送延迟同步消息。

如果是通过sendXXX方法发送即时或延时消息,最终都会辗转调用到sendMessageAtTime(@NonNull Message msg, long uptimeMillis)方法,然后调用enqueueMessage方法。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
   
        msg.target = this;// ① 设置处理该消息的handler对象
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
		// ② 设置消息类型,同步或异步
        if (mAsynchronous) {
   
            msg.setAsynchronous(true);
        }
  		// ③ 交由消息队列的入队方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }

该方法主要有3个作用,注释中的①②③分别说明了。

2、消息入队

消息入队最终是靠消息队列的恩queueMessage方法完成,其代码如下

boolean enqueueMessage(Message msg, long when) {
   
  		// ①
        if (msg.target == null) {
   
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
   
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
   
            if (mQuitting) {
   
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
			// ②
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
          	// ③
            if (p == null || when == 0 || when < p.when) {
   
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
   
                // ④
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                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;
            }

			// ⑤
            if (needWake) {
   
                nativeWake(mPtr);
            }
        }
        return true;
    }

注释中标明了5个注意点,👇一一说明下:

① 消息对象必须指定target,也就是处理消息的handler对象;而且message对象的flagFLAG_IN_USE。否则将抛出异常。

②设置消息对象标志FLAG_IN_USE和时间,创建唤醒字段,用于标记是否需要唤醒消息队列

③如果当前消息队列没有消息或要入队的消息when值小于对列头消息when值,则将新消息插入到链表头部。设置needWeak,它又由mBlocked变量决定,mBlocked的设置是在next()方法中,简单来说消息队列仅有延时消息或空队列时,mBlockedtrue

④不满足③的情况下,从消息链表头开始遍历,将新消息插入到第一个when值大于新消息when值的消息节点前方。

例如当前消息队里:100 - 30 -20 -10(数字表示消息的when值)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值