Android 消息机制,一直都是 Android 应用框架层非常重要的一部分,想更加优雅的进行 Android 开发,我想了解消息机制是非常必要的一个过程,此前也分析过很多次 Handler 消息机制, 不过都是浮于 Java 层,对于底层的源码并没有深究,经过一年的努力,笔者对于 Android 应用框架层有了新的认识和理解,这里重新写下这篇文章。
本文主要从以下几点来阐述 Androd 消息机制
我们知道, 应用进程主线程初始化的入口是在 ActivityThread.main() 中, 我们看看他是如何构建消息队列的
public class ActivityThread {
static volatile Handler sMainThreadHandler; // set once in main()
public static void main(String[] args) {
......
// 1. 做一些主线程消息循环的初始操作
Looper.prepareMainLooper();
......
// 2. 启动消息循环
Looper.loop();
}}
好的, 可以看到 ActivityThread 中的消息循环构建过程如下
接下来我们先看看准备操作中做了些什么
二. 消息循环的准备public final class Looper {
private static Looper sMainLooper; // guarded by Looper.class public static void prepareMainLooper() {
// 1. 调用 prepare 方法真正执行主线程的准备操作
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 2. 调用了 myLooper 方法, 获取一个 Looper 对象给 sMainLooper 赋值
sMainLooper = myLooper();
}
}
static final ThreadLocal
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 1.1 new 了一个 Looper 对象
// 1.2 将这个 Looper 对象写入 ThreadLocal 中
sThreadLocal.set(new Looper(quitAllowed));
} public static @Nullable Looper myLooper() {
// 2.1 通过 ThreadLocal 获取这个线程中唯一的 Looper 对象
return sThreadLocal.get();
} final MessageQueue mQueue;
final Thread mThread; private Looper(boolean quitAllowed) {
// 创建了一个消息队列
mQueue = new MessageQueue(quitAllowed);
// 获取了当前的线程
mThread = Thread.currentThread();
}}
可以看到 Looper.prepareMainLooper 中主要做了如下几件事情
好的, 可以看到在创建 Looper 对象的时候, 同时会创建一个 MessageQueue 对象, 将它保存到 Looper 对象的成员变量 mQueue 中, 因此每一个 Looper 对象都对应一个 MessageQueue 对象
我们接下来看看 MessageQueue 对象创建时, 做了哪些操作
public final class MessageQueue { private final boolean mQuitAllowed; // true 表示这个消息队列是可停止的
private long mPtr; // 描述一个 Native 的句柄 MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
// 获取一个 native 句柄
mPtr = nativeInit();
} private native static long nativeInit();
}
好的, 可以看到 MessageQueue 对象在创建的过程中, 会调用 nativeInit 来获取一个 native 的句柄, 我们看看这个 nativeInit 做了哪些操作
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
// 1. 创建了一个 NativeMessageQueue 对象
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
......
// 增加 env 对它的强引用计数
nativeMessageQueue->incStrong(env);
// 2. 将这个 NativeMessageQueue 对象强转成了一个句柄返回 Java 层
return reinterpret_cast
}
class NativeMessageQueue : public MessageQueue, public LooperCallback {
public:
NativeMessageQueue();
......
private:
JNIEnv* mPollEnv;
jobject mPollObj;
jthrowable mExceptionObj;
};
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
// 1.1 尝试调用 Looper.getForThread 获取一个 C++ 的 Looper 对象
mLooper = Looper::getForThread();
// 1.2 若当前线程没有创建过 Looper, 则走这个分支
if (mLooper == NULL) {
// 创建 Looper 对象
mLooper = new Looper(false);
// 给当前线程绑定这个 Looper 对象
Looper::setForThread(mLooper);
}
}
好的可以看到 nativeInit 方法主要做了如下的操作
可以看到这里的流程与 Java 的相反
接下来看看这个 C++ 的 Looper 对象在实例化的过程中做了哪些事情
// system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
// 1. 创建 pipe 管道, 返回该管道文件读写的描述符
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
AutoMutex _l(mLock);
// 处理 epoll 相关事宜
rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
......
// 2. 创建一个 epoll 对象, 将其文件描述符保存在成员变量 mEpollFd 中
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
// 3. 将 pipe 管道的文件读写描述符 mWakeEventFd, 添加到 epoll 中, 让 epoll 对该管道的读写进行监听
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
......
}
好的, 可以看到 Looper 实例化时做了如下的操作
可以看到这里引入了两个新的名词, pipe 管道 和 管道监听管理对象 epoll
pipe 管道: 这个管道在一个线程的消息循环过程中起到的作用非常大
epoll: epoll 机制是 Linux 为了同时监听多个文件描述符的 IO 读写事件而设计的 多路复用 IO 接口
到这里消息循环的准备工作就已经完成了, 这里分析一下它们的结构图
消息循环相互依赖关系图
三. 消息循环的启动public final class Looper { public static void loop() {
// 1. 获取调用线程的 Looper 对象
final Looper me = myLooper();
......
// 2. 获取 Looper 对应的消息队列
final MessageQueue queue = me.mQueue;
// 3. 死循环, 不断的处理消息队列中的消息
for (;;) {
// 3.1 获取消息队列中的消息, 取不到消息会阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// 若取到的消息为 null, 这个 Looper 就结束了
return;
}
......
try {
// 3.2 处理消息
msg.target.dispatchMessage(msg);
} finally {
......
}
......
}
}}
好的, 可以看到 Looper 的 loop 方法主要做了如下几件事情
好的, 可以看到获取消息的方式是通过 MessageQueue.next 拿到的, 我们接下来看看它是如何获取的
public final class MessageQueue { Message next() {
// 获取 NativeMessageQueue 的句柄
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 描述空闲事件处理者的数量, 初始值为 -1
int pendingIdleHandlerCount = -1;
// 描述当前线程没有新消息处理时, 可睡眠的时间
int nextPollTimeoutMillis = 0;
// 死循环, 获取可执行的 Message 对象
for (;;) {
// 1. 若 nextPollTimeoutMillis 不为 0, 则说明距离下一个 Message 执行, 有一定的时间间隔
if (nextPollTimeoutMillis != 0) {
// 在空闲期间, 执行 Binder 通信相关的指令
Binder.flushPendingCommands();
}
// 2. 这里调用了 nativePollOnce, 在 native 层检查消息队列中是否有 msg 可读, 若无可执行的 msg, 则执行线程的睡眠, 时间由 nextPollTimeoutMillis 决定
nativePollOnce(ptr, nextPollTimeoutMillis);
// 3. 取队列中下一条要执行的 Message 对象, 并返回出去
synchronized (this) {
final long now = SystemClock.uptimeMillis(); // 获取当前时刻
Message prevMsg = null;
Message msg = mMessages;
// 3.1 移除消息队列中无效的 Message
// Condition: msg 不为 null & msg 的处理者为 null
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 3.2 队列首部存在有效的 msg
if (msg != null) {
// 3.2.1 若当前的时刻 早于 队首消息要执行的时刻
if (now
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 3.2.2 若当前的时刻 不小于 队首消息要执行的时刻
mBlocked = false;// 更改标记位置
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
// 将队首消息返回出去
return msg;
}
} else {
// 3.3 说明消息队列中无消息, 则给 nextPollTimeoutMillis 置为 -1, // 表示可以无限睡眠, 直至消息队列中有消息可读
nextPollTimeoutMillis = -1;
}
// 4. 获取一些空闲事件的处理者
if (pendingIdleHandlerCount <0
&& (mMessages == null || now
}
// 若无空闲事件, 则进行下一次 for 循环
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
.......
}
// 4.1 处理空闲事件
......
// 4.2 走到这里, 说明所有的空闲事件都已经处理好了
// 将需要处理的空闲事件,置为 0
pendingIdleHandlerCount = 0;
// 5. 因为处理空闲事件是耗时操作, 期间可能有新的 Message 入队列, 因此将可睡眠时长置为 0, 表示需要再次检查
nextPollTimeoutMillis = 0;
}
} // native 方法
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
}
可以看到 MessageQueue.next 内部维护了一个死循环, 用于获取下一条 msg, 这个 for 循环做了如下的操作
至此一次 for 循环就结束了, 可以看到 Message.next() 中其他的逻辑都非常的清晰, 但其睡眠是一个 native 方法, 我们继续看看它的内部实现
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast
// 调用了 NativeMessageQueue 的 pollOnce
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
......
// 1. 调用了 Looper 的 pollOne
mLooper->pollOnce(timeoutMillis);
......
}
// system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
......
// result 不为 0 说明读到了消息
if (result != 0) {
......
return result;
}
// 2. 若未读到消息, 则调用 pollInner
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
......
int result = POLL_WAKE;
......
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 3. 调用 epoll_wait 来监听 pipe 中的 IO 事件, 若无事件, 则睡眠在文件读操作上, 时长由 timeoutMillis 决定
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// 4. 走到这里, 说明睡眠结束了
for (int i = 0; i
uint32_t epollEvents = eventItems[i].events;
// 5. 若为唤醒事件的文件描述, 则执行唤醒操作
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();// 唤醒
} else {
......
}
} else {
......
}
}
......
return result;
}
好的可以看到 JNI 方法 nativePollOnce, 其内部流程如下
好的, 至此线程是睡眠的机制也明了了, 这里通过 UML 图总结一下, 线程消息队列的创建与循环
线程消息的创建与循环的流程图
四. 消息的发送与处理我们都知道, Android 系统提供了 Handler 类, 描述一个消息的处理者, 它负责消息的发送与处理
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
// 调用该方法的线程的 Looper
mLooper = Looper.myLooper();
// 这里扔出了 Runtime 异常, 因此 Handler 是无法在没有 Looper 的线程中执行的
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 获取消息队列
mQueue = mLooper.mQueue;
// 给 Callback 赋值
mCallback = callback;
......
}
public final boolean sendMessage(Message msg){
......
}
public void handleMessage(Message msg) {
}
}
好的, 可以看到 Handler 的结构如上述代码所示, 本次只 focus 以下三个方法
好的, 接下来我们先看看如何发送消息的
我们先看看, Android 中的消息是如何发送的
public class Handler {
......
public final boolean sendMessage(Message msg) {
// 回调了发送延时消息的方法
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis <0) {
delayMillis = 0;
}
// 回调了发送指定时刻消息的方法
return sendMessageAtTime(msg, /*指定时刻 = 当前时刻 + 延时时间*/SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
......
// 回调了入消息队列的方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 将这个消息的处理者, 设置为其自身
msg.target = this;
......
// 调用 MessageQueue 的 enqueueMessage 执行入队列的操作
return queue.enqueueMessage(msg, uptimeMillis);
}
}
可以看到发送消息的操作, 进过了一系列方法的调用, 会走到 sendMessageAtTime 中, 表示发送指定时刻的消息, 然后会调用 enqueueMessage 执行入消息队列操作
接下来看看 MessageQueue.enqueueMessage 做了哪些操作
public final class MessageQueue {
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
msg.when = when; // 将消息要执行的时刻写入成员变量
Message p = mMessages; // 获取当前队首的消息
boolean needWake; // 描述是否要唤醒该 MessageQueue 所绑定的线程
// Case1:
// 1. p == null, 队列为空
// 2. 入队列消息需要立即执行
// 3. 入队列消息执行的时间 早于 当前队首消息执行的时间
if (p == null || when == 0 || when
msg.next = p;
mMessages = msg;
needWake = mBlocked; // 队首元素变更了, 有可能需要立即执行
} else {
// Case2: 入队列的消息执行时间 晚于 队首消息执行时间
......
// 将该消息插入到消息队列合适的位置
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when
}
// 将 needWake 置为 false, 因为该消息插入到了后面, 因此不需要唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
// 处理该消息队列绑定线程的唤醒操作
if (needWake) {
nativeWake(mPtr);
}
}
return true;
} private native static void nativeWake(long ptr);
}
可以看到 MessageQueue 在执行消息入队列时, 做了如下操作
消息入队列的过程还是很清晰明了的, 从上一篇文章的分析中我们知道, 若 MessageQueue 在当前时刻没有要执行的消息时, 会睡眠在 MessageQueue.next() 方法上, 接下来看看它是如何通过 nativeWake 唤醒的
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
// system/core/libutils/Looper.cpp
void Looper::wake() {
// 向 Looper 绑定的线程 pipe 管道中写入一个新的数据
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
....
}
可以看到, nativeWake 是通过向 Looper 绑定的线程 pipe 管道中写入一个新的数据的方式唤醒目标线程的
通过上一篇的分析可知, 当 MessageQueue.next 返回一个 Message 时, Looper 中的 loop 方法便会处理消息的执行, 先回顾一下代码
public final class Looper { public static void loop() {
final Looper me = myLooper();
......
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// 若取到的消息为 null, 这个 Looper 就结束了
return;
}
......
try {
// 处理消息
msg.target.dispatchMessage(msg);
} finally {
......
}
......
}
}}
好的, 可以看到当 MessageQueue.next 返回一个 Message 时, 便会调用 msg.target.dispatchMessage(msg) 去处理这个 msg
接下来我们看看 Handler 处理消息的流程
public class Handler {
public void dispatchMessage(Message msg) {
// 1. 若 msg 对象中存在 callback, 则调用 handleCallback
if (msg.callback != null) {
handleCallback(msg);
} else {
// 2. 若当前 Handler 设值了 Callback, 进入这个分支
if (mCallback != null) {
// 2.1 若这个 Callback 的 handleMessage 返回 true, 则不会将消息继续向下分发
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3. 若消息没有被 mCallback 拦截, 则会调用 handleMessage 进行最后的处理
handleMessage(msg);
}
} /**
* 方式一: 优先级最高
*/
private static void handleCallback(Message message) {
message.callback.run();
} public interface Callback {
/**
* 方式二: 优先级次高
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
public Handler(Callback callback, boolean async) {
......
// Callback 由构造函数传入
mCallback = callback;
......
} /**
* 方式三: 这个处理消息的方式, 由子类重写, 优先级最低
*/
public void handleMessage(Message msg) { } }
可以看到 Handler 的 dispatchMessage 处理消息主要有三种方式
好的, 消息的发送和处理到这里就分析结束了, 最后再了解一下 MessageQueue 中空闲处理者的相关知识
通过上一篇文章的分析可知 MessageQueue 通过 next 方法通过死循环获取下一个要处理的 Message, 若当前时刻不存在要处理的消息, 下次循环会进行睡眠操作
public final class MessageQueue {
Message next() {
for (;;) {
// 睡眠操作
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
Message msg = mMessages;
......
if (msg != null) {
// ......
return msg;
}
// 空闲时间
.......
}
// 空闲时间
......
}
}}
public final class MessageQueue { public static interface IdleHandler {
/**
* 处理空闲消息
*/
boolean queueIdle();
} // 空闲消息集合
private final ArrayList
synchronized (this) {
mIdleHandlers.add(handler);
}
}}
通过上述代码可以得到以下的信息
好的, 结下来看看处理细节
public final class MessageQueue {
// 空闲消息集合
private final ArrayList
// 空闲消息处理者的数组
private IdleHandler[] mPendingIdleHandlers; Message next() {
......
for (;;) {
......
synchronized (this) {
// 省略获取 msg 的代码
......
// 1. 从空闲消息集合 mIdleHandlers 中获取 空闲处理者 数量
if (pendingIdleHandlerCount <0
&& (mMessages == null || now
}
// 2 若无空闲处理者, 则进行下一次 for 循环
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
......
// 3. 将空闲消息处理者集合转为数组
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 4. 处理空闲消息
for (int i = 0; i
mPendingIdleHandlers[i] = null; // 置空
boolean keep = false;
try {
// 4.1 处理空闲消息
keep = idler.queueIdle();
} catch (Throwable t) {
......
}
if (!keep) {
synchronized (this) {
// 4.2 走到这里表示它是一次性的处理者, 从 mIdleHandlers 移除
mIdleHandlers.remove(idler);
}
}
}
......
}
}}
好的, 可以看到 MessageQueue.next 在获取不到 msg 时, 会进行一些空闲消息的处理
至此 Android 的消息机制就全部结束了, 此前分析过消息机制, 但一直没有深度到 Native 层, 只是浮于表面, 本次深入到了 Native 层, 看到了更底层的 epoll 监控管道相关的知识, 可以说发现了新的天地, 对 Handler 的机制有了更加深刻的理解, 本文中有分析的不正确或者不到位的地方, 希望大家多多批评指出, 笔者希望与大家共同成长。