Android 应用消息循环模型
本文基于Android 11 源码介绍下应用消息循环模型,我们带着下面几个问题开始:
- 一个线程有几个 Handler?
- 一个线程有几个 Looper?如何保证?
- 为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
- 子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
- 既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
- Looper死循环为什么不会导致应用卡死?
- 什么是同步消息屏障?原理是什么样的?
Handler机制相关类
- Handler:发送和接收消息
- Looper:用于轮询消息队列,一个线程只有一个
- Message:消息实体
- MessageQueue:消息队列,用于消息存储和消息管理
- Message 类对象中包含一个handler,就是该message对应的target
- Handler 类对象中包含一个MessageQueue和Looper对象,分别是Handler所在线程的MessageQueue和Looper
- Looper 类对象中包含一个MessageQueue对象,Looper对象创建的时候New的,并且和Looper进行了绑定
可以看下这个丑陋的类图:
Looper
创建Looper
创建Looper是调用Looper.prepare()方法:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
应用的主线程中系统给我们创建了Looper,所以主线程的Looper是不可以自己创建的:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Call per-process mainline module initialization.
initializeMainlineModules();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
//省略部分代码
Looper.prepareMainLooper(),注意这里主线程的Looper是不可以退出的,这是和子线程的Looper很重要的一点区别。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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通常调用的是下面的perpare方法:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
这里如何保证一个线程只存在一个Looper的 ,通过ThreadLocal来保证。
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//创建Looper时,会调用set方法,这里保证了线程和Looper的一一对应关系
sThreadLocal.set(new Looper(quitAllowed));
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
创建MessageQueue
创建MessageQueue以及Looper和当前线程绑定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper.loop()方法我们就不详细介绍了,我们可以自定义自己的超时打印,在loop方法中。
Handler
创建Handler
常见的handler创建方法:
Handler hd = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
看下构造方法:
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); // 获取looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 绑定messagequeue
mCallback = callback;
mAsynchronous = async;
}
Message
创建Message
Message 我们可以使用new的方式,但是更科学的方式是用Message.obtain(),因为这样可以检查是否有可复用的message:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
Message和Handler绑定
- 创建Message时可以使用Message.obtain(Handler h)这个构造方法绑定。
- 也可以在Handler的enqueueMessage方法中绑定
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler 发送消息
- Handler发送消息调用sendMessage方法,最终会调用enqueueMessage方法将消息放入消息队列。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
}
- 最终Looper将消息取出,派发给对应的target(Handler)处理,我们可以看到,如果callback 不为空,则直接执行callback方法,否则最后调用dispatchMessage方法处理
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
总结
我们大概总结下,消息的入队和发送,可以看下面的时序图
同步消息屏障
从上面的代码中我们可以看到,消息队列是按照时间先后顺序去取消息,如果现在有一个需要立刻马上处理的消息怎么办呢? Android引入了同步消息屏障。 那么什么叫同步消息屏障呢? 所谓的同步消息屏障,就是阻碍同步消息,只让异步消息被取出来。 那么如何开启同步消息屏障呢:
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
移除同步消息屏障:
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
同步消息屏障的应用
那么,同步屏障在系统源码中有哪些使用场景呢? Android 系统中的 UI 更新相关的消息即为异步消息,需要优先处理。 比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了 ViewRootImpl#scheduleTraversals() ,如下:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
最终会调用到下面这个函数:
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true); // 异步消息
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
HandlerThread
为了便于子线程创建Looper和保证线程安全,于是便有了HandlerThread类, 我们看下代码:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
/*
省略一万行代码
*/
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
}
它的优点就在于它的多线程操作,可以帮我们保证使用Thread的handler时一定是安全的。
扩展,关于epoll_wait
epoll_wait 等待不会消耗CPU资源,会让出CPU资源,等有事件到来时,被唤醒调度。可以查看内核手册关于epoll_wait的说明。
我们常见的主线程的trace :
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x74c41f70 self=0xf0272000
| sysTid=25129 nice=-10 cgrp=default sched=0/0 handle=0xf3f2d494
| state=S schedstat=( 3432651129 811881287 4651 ) utm=302 stm=41 core=7 HZ=100
| stack=0xff49f000-0xff4a1000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0xc0/0xcc
kernel: SyS_epoll_wait+0x270/0x384
kernel: compat_SyS_epoll_pwait+0xa0/0x16c
kernel: el0_svc_naked+0x24/0x28
native: #00 pc 00053730 /system/lib/libc.so (__epoll_pwait+20)
native: #01 pc 00025ec9 /system/lib/libc.so (epoll_wait+16)
native: #02 pc 0000f035 /system/lib/libutils.so (android::Looper::pollInner(int)+116)
native: #03 pc 0000ef43 /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+26)
native: #04 pc 000b7e0f /system/lib/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long long, int)+24)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6747)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:915)
在Android系统中有很多应用的地方,比如Input输入机制,InputReader线程中使用getEvent方法监听驱动上报的输入事件:
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
/*
省略一万行代码
*/
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
}
return event - buffer;
}
我们直接看在内核中的实现,关键点在于set_current_state(TASK_INTERRUPTIBLE),
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
int maxevents, long timeout)
{
/*
省略一万行代码
*/
if (!ep_events_available(ep)) {
/*
* Busy poll timed out. Drop NAPI ID for now, we can add
* it back in when we have moved a socket with a valid NAPI
* ID onto the ready list.
*/
ep_reset_busy_poll_napi_id(ep);
/*
* We don't have any available event to return to the caller.
* We need to sleep here, and we will be wake up by
* ep_poll_callback() when events will become available.
*/
init_waitqueue_entry(&wait, current);
__add_wait_queue_exclusive(&ep->wq, &wait);
for (;;) {
/*
* We don't want to sleep if the ep_poll_callback() sends us
* a wakeup in between. That's why we set the task state
* to TASK_INTERRUPTIBLE before doing the checks.
*/
set_current_state(TASK_INTERRUPTIBLE);
/*
* Always short-circuit for fatal signals to allow
* threads to make a timely exit without the chance of
* finding more events available and fetching
* repeatedly.
*/
if (fatal_signal_pending(current)) {
res = -EINTR;
break;
}
if (ep_events_available(ep) || timed_out)
break;
if (signal_pending(current)) {
res = -EINTR;
break;
}
spin_unlock_irqrestore(&ep->lock, flags);
if (!freezable_schedule_hrtimeout_range(to, slack,
HRTIMER_MODE_ABS))
timed_out = 1;
spin_lock_irqsave(&ep->lock, flags);
}
__remove_wait_queue(&ep->wq, &wait);
__set_current_state(TASK_RUNNING);
}
}