1、Handler成员介绍
我们先来介绍一下组成Handler机制的几位成员,以及它们常用的方法
1.1 Handler类
Handler类是应用程序开发的入口,在消息队列机制中,扮演着生产者的角色,同时还肩负着消息执行者的重担,常用的方法有:
| 方法名称 | 说明 |
|---|---|
| sendMessage()系列 | 发送普通消息、延迟消息,最终调用queue.enqueueMessage()方法将消息存入消息队列 |
| post()系列 | 提交普通/延迟Runnable,随后封装成Message,调用sendMessage()存入消息队列 |
| dispatchMessage(Message msg) | 分发消息,优先执行msg.callback(也就是runnable),其次mCallback.handleMessage(),最后handleMessage() |
1.2 Looper类
Looper在消息队列机制中扮演消费者的角色,内部持有共享的消息队列,其本质是封装对消息队列的操作,常用的方法只有两个:
| 方法名称 | 说明 |
|---|---|
| prepare() | 创建消息队列 |
| loop() | 遍历消息队列,不停地从消息队列中取消息,消息队列为空则等待 |
1.3 MessageQueue类
实际的共享消息队列,提供保存和取出消息的功能,底层由链表实现,常用方法就一个:
| 方法名称 | 说明 |
|---|---|
| next() | 获取消息,三种情况 1. 有消息,且消息到期可以执行,返回消息 2. 有消息,消息未到期,进入限时等待状态 3. 没有消息,进入无限期等待状态,直到被唤醒 |
1.4 Message类
消息的承载类,使用享元模式设计,根据API不同缓冲池大小也不同,API 4时缓冲池大小为10,常用方法:
| 方法名称 | 说明 |
|---|---|
| obtain()系列 | 获取一个消息实例 |
| recycle() | 回收消息实例 |
2、主线程为什么不用初始化Looper?
答:因为应用在启动的过程中就已经初始化主线程Looper了。
每个java应用程序都是有一个main方法入口,Android是基于Java的程序也不例外。Android程序的入口在ActivityThread的main方法中:
public static void main(String[] args) {
...
// 初始化主线程Looper
Looper.prepareMainLooper();
...
// 新建一个ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 获取ActivityThread的Handler,也是他的内部类H
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
// 如果loop方法结束则抛出异常,程序结束
throw new RuntimeException("Main thread loop unexpectedly exited");
}
main方法中先初始化主线程Looper,新建ActivityThread对象,然后再启动Looper,这样主线程的Looper在程序启动的时候就跑起来了。我们不需要再去初始化主线程Looper。
4、Handler机制Looper死循环为什么不会导致应用卡死
1)来看loop()循环。通过一个for()无限循环从MessageQueue.next()取消息,然后调用Handler的dispatchMessage(msg)把消息传递过去。
public static void loop() {
final Looper me = myLooper();//1. 首先是获取当前线程的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//2. 取出当前线程Looper中存放的MessageQueue
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { // 这里会无限循环
//3. 从MessageQueue中取消息,没有消息会被阻塞的,导致线程休眠。收到消息的时候唤醒
Message msg = queue.next();
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//4. 注意啦,这里开始分发当前从消息队列中取出来的消息,回调给Handler
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
2)接下来看下MessageQueue的next方法( 当MessageQueue中无消息时,调用nativePollOnce方法进入阻塞状态,主线程会释放CPU资源,进入休眠状态。下次收到消息MessageQueue的enqueueMessage方法调用nativeWake重新唤醒线程)
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// 1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
// 2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
// 3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时)
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//当消息队列为空时,这里会导致阻塞,直到有消息加入消息队列,才会恢复
//这里是native方法,利用的是linux的epoll机制阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
/如果target==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息。同步屏障就起到了一种优先级的作用
if (msg != null && msg.target == null) {//msg.target == null表示异步消息
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//这里比较关键 取链表头部,获取这个消息
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
.....
}
......
}
}
那这里为什么没有导致ANR或者程序死循环呢?
当MessageQueue中无消息时,queue.next()会阻塞在nativePollOnce(),这里涉及到Linux pipe/epoll机制,nativePollOnce()被阻塞时,主线程会释放CPU资源,进入休眠状态. 直到下个消息到达或者有事务发生,会通过pipe管道写入数据来唤醒主线程工作,就可以继续工作了.
这里主线程进入休眠状态和死循环是有区别的.
死循环是指主线程死锁在这里,一直执行某一块代码,无法再响应其他事件.
休眠状态是指在内核状态里,主线程被挂起,线程状态转移到休眠状态.