Handler消息机制分析

120 阅读4分钟

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中去执行,即切换到主线程中执行,这个过程就是主线程的消息循环模型