Handler消息机制分析

ThreadLocal与MessageQueue机制
本文深入解析ThreadLocal的工作原理,包括其在不同版本中的实现差异,以及MessageQueue如何通过单链表维护消息列表,实现消息的高效处理。同时,探讨了Looper与Handler在消息循环中的角色与工作流程。
ThreadLocal工作原理

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

当某些数据以线程为作用域并且不同线程具体不同的数据副本,可以考虑使用ThreadLocal

6.0以前实现

set()

public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }

首先拿到当前线程localValues,如果localValues没有值,就会初始化一个Values;
Values中有一个Object[],其实这个数组是一个以key-value的方式来存放threadLocal.set()进来的值;
在数组中key对应的是threadLocal.reference,key的index是通过 threadLocal.hash & threadLocal.mask 计算出来的值
在数组中value对应的就是ThreadLocal.set()进来的值,value的index就是key的index的下一位即index+1

key的index元素说明:reference、hash & mask
reference = new WeakReference<ThreadLocal<T>>(this);this-->ThreadLocal
hash = hashCounter.getAndAdd(0x61c88647 * 2);
    hashCounter = new AtomicInteger(0);原子操作Integer的类,高并发安全
mask = Object[].length-1
复制代码

get()

public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }
复制代码

7.0以后的实现

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);
    }

与之前不同的就是Value实现改为了ThreadLocalMap,将之前的thread.localValues改成了threadLocals;
首先拿到当前线程threadLocals,如果localValues没有值,就会初始化一个ThreadLocalMap;
ThreadLocalMap中有一个Entry[],这个Entry是一个以key-value的方式来存放threadLocal.set()进来的值;
Entry继承自WeakReference<ThreadLocal>
    key存放的就是ThreadLocal,内部还是存放了一个weakReference,泛型为ThreadLocal
    value存放的就是set进来的值
entry的index还是通过 threadLocal.hash & threadLocal.mask 计算出来的
复制代码

get()

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
复制代码
MessageQueue 工作原理
  • 通过一个单链表的数据结构来维护消息列表,单链表插入删除操作有优势;
  • enqueueMessage()就是往单链表中插入数据
  • next()是一个无限循环的方法,如果消息队列中没有消息一直阻塞,当有新消息到来时,next方法返回这条消息并将其从单链表中移除
Looper 工作原理
  • 不停地从MessageQueue中查看是否有新消息,有的话立刻处理,否则一直阻塞

  • 可以通过Looper.prepare()为当前线程创建一个looper,通过loop方法开启消息轮询

  • Looper也是可以退出的,提供了quit()和quitSafely();

    • 二者区别
    • quit直接退出
    • quitSafely设定了一个退出标记,然后把消息队列中已有的消息处理完毕退出

    Looper退出以后,handler会发送失败,send()返回false

  • loop()方法是一个死循环,唯一跳出循环的方式就是MessageQueue.next()方法返回null

  • 当looper的quit方法被调用的时候就会调用MessageQueue的quit或者quitSafely方法通知消息队列退出,否则loop()方法就会无限循环下去

  • 当没有消息时,next方法一直阻塞,导致loop方法阻塞

  • 如果next方法返回了消息,Looper就会处理,msg.target.dispatchMessage(msg)

Handler 工作原理
  • Handler发送消息的过程就是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息就开始处理,最终由Looper将消息交给Handler处理,即Handler的dispatchMessage方法
  • 检查Message的callBack是否为null,不为null就通过handleCallback来处理消息,Message的callBack是一个Runable对象,实际上就是handler的post方法所传递的Runnable参数
  • 其次检查mCallback是否为null,不为null就调用mCallback的handleMessage来处理消息,Callback是个接口
  • 最后会执行handler的handleMessage()
为什么主线程不会因为Looper.loop()里的死循环卡死?

进入死循环之前便创建了新Binder线程,创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程

ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中执行,这个过程就是主线程的消息循环模型


转载于:https://juejin.im/post/5c008863e51d454c067f426d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值