核心:通过ThreadLocal保证
Looper.prepare的时候,ThreadLocal.get如果不空报异常;否则调用ThreadLocal.set, ThreadLocal.set 会调用当前线程Thread里的ThreadLocalmap put 键值对,put 的键值对就是 Key = ThreadLocal, Value = Looper
线程里有TreadLocalMap的成员变量 Key = ThreadLocal, Value = Looper
Looper 中有一个 static final 的成员变量就是 ThreadLocal,意味着唯一性
Looper有final的成员MessageQueue
可以有多个
匿名内部类handler 默认持有外部类的activity的引用。
并且Handler.enqueMessage的时候, msg.target = handler, message持有了handler,
引用链:msg->handler->activity或者fragment
子线程 必须 Looper.prepare + Looper.loop
主线程 在ActivityThread.main方法里 帮你Looper.prepareMainLoop和Looper.loop
主线程会阻塞,主线程不能调用quit
子线程没有消息的时候,需要调用quit
这个quit作用:唤醒线程;
调用 quit 后,messagequeue返回null 退出loop
生产者消费者模式
入队满的时候 阻塞 这个handler没有限制满的情况
出队空的时候 阻塞
java的多线程里有阻塞队列,但是handler不是这样,没有限制消息个数。
nativePollOnce 到了C++层 会调用epoll_wait等待, 阻塞整个线程, CPU释放
nativeWake 到了C++层调用 epoll 的wake
通过两点来保证线程安全:
一个线程只有一个messageQueue
使用synchronized, 内置锁,加锁和释放是JVM帮我们完成的,锁的是MessageQueue 对象里面的所有函数/代码块都受限
子线程thread: handler.sendMessage->MessageQueue.enqueMessage.
MessageQueue是没有线程之说。他是一个数据结构,内存共享。
主线程loop 轮询MessageQueue。这个时候MessageQueue.next在主线程执行。
线程1-ThreadLocalMap1 唯一的ThreadLocal-looper1
线程2-ThreadLocalMap2 唯一的ThreadLocal-looper2
一个线程对应一个Looper,一个Looper 对应一个 ThreadLocal
Handler的延迟消息计算是通过SystemClock类来计算,跟系统时间没有任何关系,这是根据Handler自启动以来非睡眠正常运行时间的毫秒数和代码指定的postDelayed的时间来计算的。
通过obtainMessage
享元设计模式: 内存复用,android中使用非常广泛 如recyclerView
维持一个消息池,防止内存碎片->内存抖动->OOM
消息释放后,插入到队列头部,重置队头为这个消息节点
和anr无关。
为什么msg 阻塞不会导致anr?
这个阻塞是指线程没有事情做了,释放cpu,休眠
msg.target=null 的消息就是同步屏障
同步屏障的场景:刷新UI ViewRootImpl scheduleTraversals---> postSyncBarrier 在对头插入节点
同步屏障的删除:ViewRootImpl unScheduleTraversals---->removeSyncBarrier 删除屏障的节点
同步屏障:就是把同步消息给屏障掉了!! 然后优先执行异步消息(setAsynchrious标志)
messageQueue.next的时候
先判断 如果msg.target==null 轮询消息队列,直到找到一个异步消息为止,执行这个异步消息。
setAsynchrious(true)这个标志。
刷新UI 使用了同步屏障就是为了保证及时执行消息,如果保证不了16ms 刷新一次, 会导致掉帧。
我们平时发送到消息是同步消息
我们向主线程发送了一个UI绘制操作Message,而此时消息队列中的消息非常多,那么这个Message的处理可能会得到延迟,绘制不及时造成界面卡顿。同步屏障机制的作用,是让这个绘制消息得以越过其他的消息,优先被执行。