前言
提到handler 首先我们会想到他是用来线程之间通讯的,把子线程中的数据发送到主线程当中去做ui的更新。对于handler可以从handler, Message,MessageQueue以及loop这几个方面去讨论。
Handler
对于经常使用到handler的同学都知道 这个handler一般会创建在主线程,但handler也可以创建在子线程当中,当然如果直接创建的话会loop的空指针错误,具体为什么会报错到loop的时候我们再详细讲解。 hanlerd在发送消息的时候用到两种方式 一种是post 一种是sendMessage 。这两种都研究过会发现 post的内部还是通过send去发送这个消息。
上图是一个简单的handler发送消息的过程。查看源码可以看到 handler.sendMessage->sendMessageDelayed->sendMessageAtTime->enqueueMessage->MessageQueue.enqueueMessage到这里这个message消息才传到到MessageQueue里面。
MessageQueue
MessageQueue 是一个什么东西 他有什么意义?
MessageQueue是消息队列,相当于是一个容器,在创建looper时,同时创建的MessageQueue对象。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue里面包含了大量的message对象 当用message对象来的时候就存入 是以链表的形成存入,同时他也是无规则,无顺序的物理结构。当有message消息来了之后 代码如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Message
对呀message的理解就简单多了 ,他实现Parcelable的接口,起到对数据传递的作用。需要注意点的是:
obtain和直接new Message()的区别
消息recycle()后,会被缓存到静态变量sPool中。spool是一个单链表。使用obtainxxx()会直接从sPool中取第一个message来复用。避免了重复创建过多消息对象。
代码如下:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}`
Looper
到了最重要的一个环节 looper。对于looper来说他的意义就是在不停的循环 拿取消息 将消息发送到handler当中。
在ActivityThread中创建
public static void main(String[] args) {
…
Looper.prepareMainLooper();
…
Looper.loop();
}
在创建的时候在 Looper.prepareMainLooper();中调用prepare()创建Looper对象 同时创建messageQueue对象
所以在一个线程当中之能有一个looper 和messageQueue对象 可以有多个handler对象。对个handler对象公用一个looper和messageQueue对象。
looper里面还包含一个死循环 那么这个死循环 会不会造成应用的卡死?会一直消耗cpu吗?
首先第一点应用会不会卡死 卡死就是anr(application not responese)了呗 。对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程肯定不能运行一段时间后就自动结束了,那么如何保证一直存活呢??简单的做法就是可执行代码能一直执行下去,死循环便能保证不会被退出,例如:binder线程也是采用死循环方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单的死循环,无消息时会休眠,但是死循环又如何处理其他事物呢??通过创建新的线程。真正卡死主线程操作的是在回调方法onCreate、onStart、onResume等操作时间过长,会导致掉帧甚至ANR,Looper.loop()本身不会导致应用卡死。
主线程的死循环一直运行会不会特别消耗CPU资源呢?其实不然这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。