Android 消息机制 handler 源码分析原理

整篇文章分为三大块。

1、handler消息机制流程图

2、ThreadLocal

3、源码分析整个发送消息的过程,包括阻塞、唤醒、延时加入消息队列。

上图为网上获取,感谢提供。

 

以上模型的解释:

  1.以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。

  2.Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。

  3.在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。

 

这里从图中可以看到参与消息处理有四个对象,它们分别是Handler, Message, MessageQueue,Looper。

 

ThreadLocal的工作原理

定义:ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有再指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。

重写方法测试一下

我们进入get方法看看

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

 

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

private T setInitialValue() {
    T value = initialValue();//我们重写的方法  value = 特兰克斯
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);//此时创建ThreadLocalMap
    return value;
}

再创建一个子线程试一试:

通用打印的是特兰克斯,因为

从ThreadLocalMap 里取值 key=thread1, 没有值, 所以也走的create初始化

如果我们设置set方法 会怎么样呢?

set方法

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);//这次已经有值了
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

 

正式进入hanlder

一、启动app,创建全局唯一的Looper对象和全局唯一的messagequeue

app → ActivityThread.main()执行 → Looper.prepareMainLooper() → prepare() → sThreadLocal.set(new Looper(quitAllowed));

→ 创建全局唯一的MessageQueue

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

TheadLocal里创建了全局唯一的looper对象

小结:app启动做的一些初始化的事情

二、Activity中创建Handler

在创建Handler的时候,会调用Looper.myLooper(); 拿到全局唯一主线程的looper对象。

小结:

三、发送消息 sendEmptyMessage  最终调用enqueueMessage()

小结:

四、消息处理

ActivityThread.main()方法里 Looper.loop();

小结:

五、分析消息阻塞(阻塞时间)、唤醒、延时入队

next方法里 阻塞休眠

enqueueMessage方法里 唤醒

Looper 的阻塞主要是靠MessageQueue 来实现的,在next()@MessageQuese进行阻塞,在enqueueMessage()@MessageQueue 进行唤醒。主要依赖native 层的Looper 依靠epoll 机制进行的。

阻塞和延时,主要是next()中nativePollOnce(ptr, nextPollTimeoutMillis)调用naive方法操作管道,由nextPollTimeoutMillis决定是否需要阻塞nextPollTimeoutMillis为0的时候表示不阻塞,为-1的时候表示一直阻塞直到被唤醒,其他时间表示延时。

简单理解阻塞和唤醒

就是在主线程的MessageQueue没有消息时,便阻塞在loopqueue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。

这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

六、延时入队

主要指enqueueMessage()消息入列是,上图代码对message对象池得重新排序,遵循规则(when从小到大)。

此处for死循环推出情况分两种

第一种:p==null表示对象池中已经运行到了最后一个,无需再循环。

第二种:碰到下一个消息when小于前一个,立马推出循环(不管对象池中所有message是否遍历完),进行从新排序。

对象池

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值