安卓给进程的程序运行设计了一套基于消息的循环处理机制,应用程序的主线程正是通过这个消息处理机制完成四大组件的启动和销毁,完成Input事件的处理和界面渲染(即vsync信号的处理)等工作。
1. 一图介绍原理
主要角色
- 消息队列(MessageQueue):储存消息
- 消息(Message):每个消息对应一个Handler,在Handler中定义了消息该怎么处理。 消息包括同步消息和异步消息,如果设置了barrier,那么异步消息会优先处理。
- Handler:消息处理者, 重要子类有H(处理四大组件的启动),FrameHandler(处理vsync信号),ViewRootHandler(input事件的处理)
- Looper: 负责循环从消息队列中取下一个消息,交给对应的Handler去处理
- 消息生产者:一个消息的来源,包括AMS,IMS, SurfaceFlinger等
运行机制
看图就很明白了,运行机制就是Looper循环从消息队列中取下一个消息,交给对应的Handler去处理。
2. 主要类图
3. 运行流程
下面结合android14版本的代码介绍运行流程,代码部分只贴了重要的代码,和本文内容无关的代码省略了。
3.1 初始化流程
初始化这套消息处理机制,主要是在Looper类中进行的,如果是主线程初始化Looper()需要调用prepareMainLooper方法, 其他线程初始化Looper需要调用prepare()方法。在初始化的过程中会创建MessageQueue对象。 下面是初始化过程的代码:
public static void prepareMainLooper() {
// 其实主线程的初始化方法prepareMainLooper最终调用prepare(boolean quitAllowed)方法
prepare(false);
...
}
//
public static void prepare() {
// 非主线程的初始化方法prepareMainLooper最终也会调用prepare(boolean quitAllowed)方法
prepare(true);
}
// quitAllowed的意思是是否可以推出循环, 主线程因为一直需要处理消息,所以不能推出循环,而子线程可以退出
private static void prepare(boolean quitAllowed) {
// 如果当前线程没有Looper对象,就创建一个新的Looper, 在Looper的构造函数中会创建MessageQueue对象。Looer对象用ThreadLocal储存,保证了线程单例
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Looper初始化好了之后接下来就需要开发循环处理消息了,这一步是通过调用Looper.loop()来实现,下面接着看这个流程
3.2 消息处理流程
loop()方法里面会写一个for循环,然后每个循环体内从消息队列中获取一个消息,交给对应的Handler去处理。
public static void loop() {
final Looper me = myLooper();
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
// 这个函数的返回值表示是否要继续循环, 返回true表示上面的循环继续下去,false则跳出循环
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 这里调用MessageQueue.next()方法去获取下一个消息
Message msg = me.mQueue.next(); // might block
try {
// msg的target属性对应一个handler
msg.target.dispatchMessage(msg);
}
// 一般情况下都是返回true, 如此实现循环处理消息
return true;
}
接下来看看如何从消息队列获取下一个消息,即MessageQueue.next()的内部实现,然后继续跟下Handler如何处理消息,即Handler.dispatchMessage()的内部实现
取下一个消息的时候如果设置了同步屏障,会优先取异步消息,然后再取同步消息,所有消息都处理完之后,再处理IdleHandler。消息和IdleHandler都处理完之后,这个方法也不会返回,而是调用nativePollOnce方法进入阻塞状态,直到新的消息添加进来线程被唤醒,继续按照上面的逻辑取下一个消息。
// 获取下一个待处理的消息
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
// 如果消息队列里面没有消息,并且所有的idleHandeler也处理完了,
// 那么就调用这个函数进入阻塞状态,直到被唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
// 第一个消息的target为null表示这个消息是一个同步屏障,这种情况下取出第一个异步消息,
// 否则取消息队列的第一个消息即可
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
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 {
// 如果当前消息的处理时间已经到了,那么把这个消息返回给looper去处理
return msg;
}
} else {
// 消息队列的头部为空,表示消息队列是空的,那么继续下面处理IdleHandler
nextPollTimeoutMillis = -1;
}
}
// 处理所有的IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
boolean keep = false;
try {
keep = idler.queueIdle();
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
}
Handler.dispatchMessage(Message msg)的实现就很简单了,就是调用Message中定义的callback, 或者Handler对象中的handleMessage方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
3.3 添加消息
想消息队列添加一个消息的时候,如果消息队列里面有消息,只需要添加到队列尾部即可,如果消息队列里面没有消息,还需要用epoll机制唤醒主线程去处理消息。 发送消息是调用Handler.sendMessage(@NonNull Message msg)来完成,我们下面看看这个方法的具体流程
public final boolean sendMessage(@NonNull Message msg) {
// 内部会调用sendMessageDelayed方法
return sendMessageDelayed(msg, 0);
}
// delayMillis 参数的意思是消息的延迟处理时间
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
// 这里最终会调到MessageQueue.enqueueMessage(Message msg, long when)方法
return enqueueMessage(queue, msg, uptimeMillis);
}
// 我们接着看MessageQueue.enqueueMessage(Message msg, long when)方法
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.when = when;
Message p = mMessages;
boolean needWake;
// 如果消息队列没有消息那么把needWake属性置为true,后面根据这个属性判断是否需要唤醒线程
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 {
// 如果消息队列有消息那么把新消息插入到消息队列,同时判断是否需要唤醒主线程
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;
}
// 这里用nativeWake方法唤醒主线程,前面介绍过在MessageQueue.next()方法中,如果消息队列没有消息会调用nativePollOnce进行阻塞状态等待被唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
4. 一些需要注意的细节
1. 同步屏障需慎用
同步屏障用的时候需要注意,如果异步消息设置了延迟处理,并且此时有同步消息,那么这些同步消息不会得到及时处理,而是需要等异步消息处理完之后才能被处理。
2.如何保证消息队列的线程安全
可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程) 添加消息的方法enqueueMessage()中有synchronize修饰,取消息的方法next()中也有synchronize修饰。
5. 总结
Looper消息循环处理机制是安卓系统得以运行的重要机制。很多功能的实现依赖这个机制,包括应用启动,vsync信号的处理,input事件的处理,后面的文章会接着介绍。
参考
android14源码