深入理解Android消息机制的原理

本文详细介绍了Handler、Looper和MessageQueue之间的关系,包括它们如何协同工作,消息的优先级处理,以及主线程中的特殊性。还讨论了ThreadLocal的作用和消息分发的核心loop方法,涉及epoll机制。
摘要由CSDN通过智能技术生成

Handler & Looper & MessageQueue关系简述

  • 一个线程至多有一个looper;一个looper有一个mq;一个mq对应多个message;一个message对应多个handler。
  • 消息类型:同步、异步、同步屏障消息。
  • 无限循环:在队列中没有消息时,当前线程进入阻塞状态,当有消息进来时才唤醒该线程,因此不会占用cpu的资源。
    在这里插入图片描述

消息分发的优先级:

  1. Message的回调方法:message.callback.run(),优先级最高
  2. Handler的回调方法:Handler.mCallback.handleMessage(msg)
  3. Handler的默认方法:Handler.handleMessage(msg)
        //1.直接在runnable中处理任务
        handler.post {

        }
        
        //2.使用Handler.Callback来处理消息
        val handler:Handler = object :Handler(Callback {
            return@Callback true
        })
                
        //3.使用handleMessage处理消息
         val handler = object :Handler(){
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
            }
         }
  • 为什么主线程中创建Handler没有绑定looper,也能发送消息呢?

    • 在ActivityTread.main()方法中调用了Looper.prepareMainLooper()方法创建了主线程的Looper,然后调用了loop()开启消息队列轮询
    • 通常我们认为ActivityTread就是主线程,实际上它并不是一个线程,而是主线程操作的管理者
class Looper{
    //相当于map,key时线程,value是looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
      //一个线程只能有一个Looper,prepare不能重复声明
       private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

      
       public static @Nullable Looper myLooper() {
        //获取当前线程的looper
        return sThreadLocal.get();
    }

}

在这里插入图片描述

//使用post方法消息入队,最后会把runnable包装成一个message
  public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

//使用享元设计模式,提高内存的利用率

  public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

//当消息使用完,都会调用这个方法回收消息
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

消息入队,最后都会调用到enqueueMessage方法中,enqueueMessage方法的执行逻辑分为两个部分:

1. 如果新消息的被执行时间when比队列中任何一个消息都早,则插入队头,唤醒Looper。
2. 如果当前队头是同步屏障消息,并且新消息是异步消息,则唤醒Looper。
  • postSyncBarrier同步屏障消息(该方法无法被开发者调用)

    • message.target=null,这类消息不会被真的执行,它起到了flag标记的作用,mq在遍历消息队列时,如果队头是同步屏障消息,那么会忽略同步消息,优先让异步消息得到执行。这就是它的目的,一般异步消息和同步消息会一同使用。

    • 异步消息&同步屏障 使用场景

      • ViewRootImpl接收屏幕垂直同步消息事件用于驱动UI绘制
      • ActivityThread接收AMS的事件驱动生命周期
      • InputMethodManager分发软键盘输入事件
      • PhoneWindowManager分发电话页面各种事件
  • 消息分发
    消息分发核心方法在于loop方法,loop方法里面会的调用的linux里的epoll机制。handler最终都是调到adk(c/c++)层。epoll会把哪个流发生了怎样的I/O事件通知我们。此时我们对这些流的操作都是有意义的。

ThreadLocal

  • ThreadLocal提供了线程独有的局部变量存储能力,可以在整个线程存活的过程中随时取用。
  • 注意ThreadLocal没有线程安全的能力。
    在这里插入图片描述
    多个线程操作上述的local对象的usr对象,进行数据修改之后,user变量会更改。

面试经典问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值