简要概述一下:
hander机制由这么几个东西组成:Looper、MessageQueue、Message、hander。如果说Handler机制是第一个传送带,那么MessageQueue就是传送带,Message就是传送的物品,Looper就是提供动力的箱子
Looper:
- Android环境下通过Looper.prepareMainLooper():里面还是调用的prepare(false)方法,这个false表示不可退出标志为。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
- Looper.prepare():通过prepare(true)创建Looper对象,并将实例set到threadLocal中。需要手动调用mylooper()获取looper对象。
public static void prepare() {
prepare(true);
}
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));
}
- Looper.looper():开启循环
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue; 获取到looper绑定的消息队列
......
for ( ;; ) {
Message msg = queue.next(); // 如果当前队列为空,则阻塞队列,na因为next是syn同步块
if (msg == null) {//如果对象为空直接退出
return;
}
......
msg.target.dispatchMessage(msg); //核心:通过dispatchMessage(msg)分发消息;
......
msg.recycleUnchecked(); //清空对象中的字段
}
}
Handler :
第一步:new handler的时候拿到当前线程的looper对象和looper绑定的MessageQueue对象。
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
......
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
第二步:提交msg的方法:所有的sendMessage最终都是调用sendMessageAtTime(msg,time)方法,然后将handler实例对象封装到msg对象中,通过queue.enqueueMessage()方法入队。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
........
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
........
return queue.enqueueMessage(msg, uptimeMillis);
}
第三步:通过dispatchMessage方法对收到的消息进行分发;
vpublic void dispatchMessage(Message msg) {
if (msg.callback != null) { //如果Message中封装了callback方法
handleCallback(msg);
} else {
if (mCallback != null) { //如果new handler的时候传入了callback方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 如果在handler中重写了handleMessage方法
}
}
Message:
消息对象,其中的target是用来绑定handler的,数据结构是栈结构,先进后出
方法一:Message.obtin() ,入栈操作。先 复用Message,没有在创建一个。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
方法二: recycle(),清空并入栈
void recycleUnchecked() {
flags = FLAG_IN_USE;
清空数据。。。
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
MessageQueue:
单链表式的消息队列;
Looper.prepare() —> new Looper(boolen) —> new MessageQueue(boolen) —> int nativeInit() 由native层创建一个native层的MessageQueue队列,将地址返回存在java层。
消息入队方法:
boolen enqueueMessage(msg , time) :通过synchronized(this)阻塞,通过判断比较time,根据time的先后顺序入队。
消息出队方法:
Message next():根据msg中的when阻塞队列的等待时间。通过.next判断是否有下一个,依次获取。
阻塞方法:
nativePollOnce() —> nativeMessageQueue.pollOnce() —> 底层MessageQueue中的looper对象中的pollOnce(timeoutMillis) —> pollInner(timeoutMillis) —> int eventCount = epoll_wait()
总结:
Java层的阻塞是通过native层的epoll机制来实现的因此looper死循环不会造成UI阻塞。阻塞结果三种:eventCount <0:错误返回;eventCount =0:超时返回;eventCount > 0消息事件返回。
为什么每个线程中调用Looper.myLooper()可以返回各自线程的Looper
这里涉及到ThreadLocal的数据隔离的原理(ThreadLocal的实现原理.)
- ThreadLocal为每一个线程提供了独立的数据副本
如何保证每个线程的looper、MessageQueue的唯一性
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));
}
- 在调用Looper.prepare()方法的时候,先获取looper对象,如果存在直接抛出异常,否则就创建Looper对象并赋值到ThreadLocal中去
- 创建Looper对象的时候,构造中创建了MessageQueue对象
MessageQueue如何阻塞和唤醒队列
- messageQueue在next方法中获取消息,如果msg为空,那么,在nativePollOnce方法中传入nextPollTimeoutMillis = -1,是线程在native层处于一直挂起的状态
nativePollOnce(ptr, nextPollTimeoutMillis);
- 当往队列中插入数据的时候,needWake = mBlocked;由于队列处于阻塞状态,那么mBlocked为true,最后调用nativeWake的方法,从native层使得队列被唤醒
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
......
}
if (needWake) {
nativeWake(mPtr);
}
主线程一直Looper为什么不会产生卡死?
- Looper是在ActicityThread的main入口函数中创建,并在方法的最后一步开启looper(),放在最后一步也就为了将ActivityThread先创建出来并attach()好,将该完成的工作都完成比如说binder机制。
- 根据Hendler机制,Looper在prepare的时候,先new了一个looer然后将其放入ThreadLocal中,ThreadLocal的作用就是线程独有,因此Looper最多只会阻塞当前主线程,进程间通信可以binder执行,binder是通过创建新线程去完成通信任务的,当接收到进程间的消息后,通过handler机制添加到mq中。
- Looper循环中只做了一件事,就是从MessageQueue队列中获取消并通过Handler机制分发出去并处理,获取消息的真正实现是在C层的MessageQueue中,底层是通过pipe/epoll机制实现的。
- 当messagequeue队列空闲时,通过epoll_wait()方法进行阻塞,阻塞在pipe管道的读端,当有message消息添加进来时,pipe管道的写缓存不为空,epoll的触发机制将发送指令触发写操作,epoll_ctl()监听并返回结果。Looper将被唤醒,并从队列中读取结果,最终消息从message队列中获取并通过handler机制分发出去。
pipe/epoll机制:
工作原理:
Linux来说一切皆文件。Epoll机制有三个方法,epoll_create()、epoll_ctl()、epoll_wait();
- epoll_create():创建epoll句柄,使用mmap机制(1拷贝)加速内核和用户之间的数据传递,红黑树和和就绪链表,如果新增socket句柄,将在就绪链表中查找,有就返回,没有就添加进去;
- epoll_ctl():epoll事件的注册函数,对事件添加增删改查的功能,以及监听器
- epoll_wait():epoll的等待超时机制,返回值-1:错误(永久阻塞),0:超时,>0正常
Epoll触发机制:
触发机器也就是通知机制,分两种:水平触发(level-triggered)+边缘触发(edge-triggered)。
level-triggered:当缓存区中数据由空变不空时,将触发可读信号,直到读完为止;当缓存区中数据由满变为不满时,将触发可写信号,直到写满为止。
edge-triggered:当缓存区中数据由空变不空时,将触发可读信号一次,程序将执行轮询读操作,直到读到EGAIN为止;当缓存区中数据由满变为不满时,将触发可写信号一次,程序将执行轮询写操作。