Handler机制
Handler机制几乎贯穿了整个应用从启动到结束,包括Throwable抛出异常、View绘制、事件分发、Activity启动、Activity生命周期等都涉及到Handler机制。
我们知道,主线程中不建议耗时操作,子线程中不允许更新UI,会可能导致ANR。所以,我们需要,能在子线程中做完耗时操作,然后去到主线程更新UI的办法。消息机制,即消息的发送、入队、出队、分发过程。
Android 消息机制涉及了四个部分:
- Handler: 消息的发送者和处理着
- Message: 消息的载体
- MessageQueue: 消息队列
- Looper: 消息循环体
消息机制流程如下:
- 主线程通过prepare创建一个Looper对象,且规定只允许关联一个Looper。
- MessageQueue是依赖Looper一起创建的。Thread = Looper = MessageQueue。
- 调用Looper.loop()方法,进行消息循环获取
MessageQueue
中的Message对象
,如果消息为空就会进行线程阻塞。 - 创建Handler对象后,自动绑定Looper对象和MessageQueue队列。
- 然后调用与
Message
绑定的Handler
对象的dispatchMessage
方法实现接口回调,从而更新handle所在的主线程的UI。
Looper轮询器
public static final void main(String[] args) {
//1、调用prepare()方法创建Looper对象并把该对象setMainLooper()绑定到当前线程中
Looper.prepareMainLooper();
...
//2、主线程中调用Looper.loop(),开始轮询,取消息
Looper.loop();
}}
Looper.prepareMainLooper()方法:
public static final void prepare() {
//保证当前线程只能存在一个Looper对象,如果已经存在就会抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());//把Looper绑定到当前线程
}
//Looper这个类的构造方法是private私有的,不允许外界直接new出来Looper对象。
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
请注意!MessageQueue是依赖Looper一起创建的。
Looper.loop()方法轮询消息:
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); //1、messageQueue获取消息
if (msg != null) {
...
msg.target.dispatchMessage(msg);// 2、handle 处理消息
msg.recycle(); // 3、message回收
}}}
Handler 处理者
在构造方法中获取潞当前线程的Looper和MessageQueue。
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
//handler.post(runnable) 、view.post(runnable)
<!--最后runnable都会封装成Message,放入队列-->
发送消息:send方法(post方法最终也会调用send方法)
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;//注意
sent = queue.enqueueMessage(msg, uptimeMillis);
}else {
RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue");
}
return sent;
}
处理消息:dispatchMessage、handleCallback
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
mCallback.handleMessage(msg)
return;
}
handleMessage(msg);
}
}
<!--分发调用-->
private static void handleCallback(Message message) {
message.callback.run();
}
Message 和 MessageQueue
Message内部构建一个链表维护被回收的Message对象,当调用obtain方法时会优先从池中获取,如果池中没有则创建新的Message对象,同时在使用完毕之后,进入池中以便于复用。
obtain方法创建Message对象的流程:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
在Looper.loop()方法,使用完毕时候调用了Message的recycle()方法。
MessageQueue主要包含两个操作:
- 插入消息(enqueueMessage)
- 读取消息(next)
插入消息(enqueueMessage):通过单链表(插入和删除效率高)的数据结构来维护消息列表。通过单链表的插入操作,根据时间看当前发送的Message是否需要马上处理。
触发时机:Handler发送消息的时候调用。
final boolean synchronized enqueueMessage(Message msg, long when) {//新消息msg
Message p = mMessages;//当前消息头节点 p
if (p == null || when == 0 || when < p.when) {
msg.next = p;//队列为空||时间最小,插入到头部
mMessages = msg;
needWake = mBlocked; // 队列阻塞状态
} else {
Message prev = null;
while (p != null && p.when <= when) {//延迟时间越大,排到队尾
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; //无需唤醒
}
}
if (needWake) {
nativeWake(mPtr);//唤醒
}
return true;
}
读取消息(next):采用阻塞的方式去获取消息队列中的消息,一旦有消息立即返回并且将它从单链表中移除;如果没有消息就一直阻塞。
final Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {//阻塞线程for (;;)
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {//延迟时间 == 当前时间
mBlocked = false;
mMessages = msg.next;
msg.next = null;
return msg;
} else {//取最小值:剩下延迟时间-循环阻塞
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
}
dispose();
return null;//Looper.quit(),返回null。
}
}
Looper.loop()为什么不会阻塞主线程?
-
Looper 中的 loop()方法, 他的作用就是从消息队列MessageQueue 中不断地取消息, 然后将事件分发出去。
- loop()里的消息队列里next中的耗时操作,本身并不会导致主线程卡死。导致主线程卡死的真正原因是耗时操作之后的触屏操作, 没有在规定的时间内被分发。
IdHandler(闲时机制)?
是 Handler 机制提供的一种,可以在 Looper 事件循环的过程中,当消息队列为空或延迟消息还未执行的时候,执行任务的一种机制。适用于启动优化的场景,将不必要的操作在页面绘制完成之后执行
postDelay()、post()与sendMessage()区别?
post方法一般用于异步操作,通过传入runnable对象处理逻辑。post方法最终也会调用send方法,将runnable封装成message对象,调用queue.enqueueMessage(msg, uptimeMillis)插入消息队列中后,根据延迟时间放在阻塞单链表中,只不过post延迟时间为0。sendMessage()一般用于多线程通信,在子线程调用该方法并传入自己创建Message对象实例,handle在主线程实现handleMessage接口回调,处理message携带的数据逻辑。