本文涉及到的源码都以 Android Code Search 提供的最新的为准
由浅入深了解 Handler
Handler 面试必备选项,不管是什么等级的面试,几乎百分百会面试到Handler。
Handler 的核心功能贯穿了 framework ,native 和 kernel 。接下来我们就从 framework 再到 native,一直到深入到 kernel 层开始对 Handler 进行分析。
Handler 是什么?
如果要学习Handler,不能将Handler单独拎出来学习。需要结合其它的相关类一起学习。
在 Android 中 Handler 是一种消息机制,是一套线程异步通信框架,Android 各个组件启动过程中的交互都是离不开它,在Android地位非常之高,不亚于Binder。
Handler 机制在 java 层涉及到了MessageQueue、Message、Looper、Handler 这4个类。
- Message:见名知意,Message 类就是消息机制中的传递消息。这个消息,分为硬件产生的消息和软件产生的消息。
- MessageQueue:消息队列,消息的主要存储容器,其主要功能是投递消息和取走消息。
- Looper:可以叫它轮询器,它有一个 MessageQueue,它会不断的轮询 MessageQueue 里的消息,按一定的分发机制将消息分发给目标处理者。
- Handler: 消息的辅助类,它包含一个 Looper 和一个 MessageQueue 。它主要有两个功能,一个是向消息队列中发送各种消息、事件,另一个是从消息队列取出并处理相应的消息、事件。
MessageQueue 在Android中一般是跟线程绑定的,每个线程可以有一个属于自己的MessageQueue,对应的MessageQueue 会有一个唯一的Looper来进行消息的轮询。
单独使用Handler时,当我们新创建一个Handler对象,并且将它和一个Looper绑定,Handler传递的消息、事件会进入到当前的这个 Looper 对象的 MessageQueue中,并且消息也在 Looper 所有的线程响应或者执行。
在Android应用中,UI线程也就是主线程,它有一个专用的消息队列。上层应用的四大组件消息传递和一些窗口的创建都要依赖于这个消息队列。我们也可以自己创建自己的线程,通过 Handler 与主线程通信。
Handler 的基本原理
先看下面的图:
首先我们要明确一点,一个 APP 会运行多个线程,每个线程可以创建多个 Handler,不同的线程之间可以获取对方的 Handler 对象。
当 Handler 通过 send/post 相关的方法发送一个消息出去,这个消息就会跟 Handler 间接通过 MessageQueue.enqueueMessage(Message, long) 将消息入队。在消息入队之前会根据先后顺序和延迟时间的长短排序,然后根据排序再将消息加入队列。同时在APP启动时,会通过 ActivityThread.main() 中调用Looper.prepareMainLooper() 和 Looper.loop(),这样 Looper 这个轮询器就开始轮询了,Looper 通过一个死循环一直轮询,通过 MessageQueue.next() 一直尝试去消息队列里取出消息,如果为空就 return 掉,如果不为空并且 Message.target 不为空,就通过 Message.target.dispatchMessage() 将消息传到 Handler 的 handleMessage() 。这样我们就可以处理到相应的消息了。
从源码看 Handler
Handler 的创建
最简单的获取 Handler 对象的方式就是通过 new Handler()
。
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在无参构造里调用了重载构造方法,并传入了 null, false。然后在构造给全局的 mLooper,mQueue,mCallback 和 mAsynchronous 标识赋值。
Looper
上面的 mLooper 和 mQueue 的赋值都是通过 Looper 获得的。先看 Looper.myLooper():
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
然后再看 Looper 的构造:
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在 Looper 构造中就已经构建好了 Looper 的 MessageQueue 。Looper 的构造函数是私有的,那么它是在哪里调用的呢?接着看代码:
private static Looper sMainLooper;
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 通过线程本地变量 sThreadLocal 保存 new Looper(),将 Looper 与 当前线程绑定到一起
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 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();
}
}
在 Looper 中提供了三个 Looper 初始化的方法,可以很清楚看到,Looper 的初始化最后都会调用 prepare(boolean)
。这样的统一入口,能很好地控制 Looper 对象的构建,在通过 ThreadLocal 就可以保证每个线程只创建一个 Looper。
Looper 的初始化
Looper 的初始化是在 APP 启动时,,通过 ActivityThread 的 main 方法中,也就是新的 APP 进程的入口方法中调用的。我们看代码。
ActivityThread 的main() ,这里只讨论 Looper 的初始化,其他的代码就先省略掉:
public static void main(String[] args) {
// 创建一个 Looper 对象,并且将 Looper 与当前线程绑定到一起。
Looper.prepareMainLooper();
// ...省略...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// ...省略...
// 调用 Looper 的 loop() 方法,开启无限循环,不停的轮询 MessageQueue 的消息
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Activity 运行在 UI 线程,我们在任意一个 Activity 里,调用Looper.prepre() ,一定会导致崩溃,就是因为上面的一系列操作,保证了在了一个线程中只能存在一个 Looper 对象,那么也保证了每个线程也只有一个 MessageQueue。并且在 prepareMainLooper()
中 quitAllowed
都传入的 false
,保证了 UI 线程中的 Looper是不可退出的。
具体看 Looper 的构造和 quit() 方法:
// 传入了 quitAllowed,用来设定 Looper 是否可以退出
private Looper(boolean quitAllowed) {
// 实际上 quitAllowed 控制的是 MessageQueue 中消息的存取是否可以中断
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// Looper.quit() 实际上也是调用的 MessageQueue.quit()
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
我们看 MessageQueue 的构造和 quit(boolean)
private final boolean mQuitAllowed;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
void quit(boolean safe) {
// 如果不允许退出,就会直接抛出异常
// 如果我们在 Activity 调用 Looper.myLooper().quit(),就会抛出下面的异常
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
小结:
- 在 APP 启动时,就会在 ActivityThread.main() 中调用 Looper.prepareManiLooper() 创建 UI 线程中的 Looper 对象,
- 调用 Looper.prepareManiLooper() 的同时设置了 Looper 不可退出,同时间接的控制了 MessageQueue 的不可退出。
- 在创建Looper对象的同时也将 Looper 对象与当前线程通过 ThreadLocal 绑定到一起,保证了每个线程只能有一个 Looper对象。而 MessageQueue 是在 Looper 在初始化时创建的,这样也保证了 MessageQueue 的唯一性。
发送消息
Handler 不管是 post 还是 send 相关的方法最后一定会调用 MessageQueue.enqueueMessage() 方法的。
我们简单看一下 Handler的源码
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
/** send/post 相关的方法都会间接调用到这个方法 */
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
// 判断是否为异步消息,异步消息将不会受到同步屏障的影响
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 最终调用到MessageQueue去插入消息
return queue.enqueueMessage(msg, uptimeMillis);
}
这样看起来,Handler 就是一个工具类,它提供了一些入口函数,消息的管理最后要委托给MessageQueue处理。
接下来看 MessageQueue 源码 中 的 enqueueMessage() 方法:
boolean enqueueMessage(Message msg, long when) {
// msg.target == null 表示该 message 是一个同步屏障,同步屏障的设置不是通过该方法实现的,所以会先判断是否存在同步屏障。
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 加锁,保证多个线程给 MessageQueue 发消息时保证线程安全
synchronized (this) {
// 判断入队的消息是否已经使用过了,确保消息是新的。
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
// 判断当前 Looper 的轮询是否正在终止,如果正在终止,后面入队的消息不会得到处理,并且在 Android 中如果Looper的轮询已经终止,那么也就标志这当前的线程也没有什么实际作用了,尤其是在 UI 线程。如果我们自己创建的线程用的 Looper,一般是需要允许 Looper 退出的,就不会走到这一步了。
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;
}
// 标识该消息已经使用,如果同一个消息 send/post 两次及以上就会在上面抛出异常。
msg.markInUse();
// 消息执行的时间(手机启动距现在的毫秒数 + delay的毫秒数)
msg.when = when;
// mMessages 用来记录下一条消息
Message p = mMessages;
boolean needWake;
// mMessages == null 表明当前队列没有消息;
// when==0 表示没有延迟;
// when<p.when 当前消息预计的执行时间小于已经使用过的Message的执行时间,那么之前队列的消息都已经执行完毕
// 综上条件,当前的队列为空或者已经没有可执行的消息了,所以当前需要入队的消息作为消息队列头
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 {
// 是否需要唤醒队列进行取消息操作的条件判断
// mBlocked==true 表示队列已经空了,next()已经阻塞了,正在等待 pollOnce()
// isAsynchronous 表示消息是否为异步消息
// p.target == null 表明前一条消息是一个同步障碍(同步障碍之后消息不再被处理),在进入方法的第一行就对当前入队消息进行的target的判断,能走到这里,说明它不是同步障碍。
// 当队列为空,当前消息是一个异步消息,并且当前需要入队的消息不是同步障碍,就需要唤醒队列轮询,在MessageQueue.next()中可以进行取消息的操作。
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) {
// 在native中唤醒队列。
nativeWake(mPtr);
}
}
return true;
}
接下来看native层的 MessageQueue 中的 nativeWake:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
// 在MessageQueue创建时,会对 NativeMessageQueue 进行初始化,ptr 是 NativeMessageQueue的 的指针
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
在 nativeWake() 中又调用了 NativeMessageQueue 的 wake() 方法
void NativeMessageQueue::wake() {
// native 层的 Looper,将唤醒队列的操作交给 native 层 Looper。
mLooper->wake();
}
继续深入到 Looper.cpp 的 wake() 方法:
void Looper::wake() {
// log 不去管
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
// 通过 eventfd 的 write() 向文件描述付为 mWakeEventFd 的设备写入,用来唤醒监听着的 epoll ,如果不成功会一直发
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
到这里 Handler 发送消息的主要工作就说完了;总结一下
- 在 Java 层 Handler 通过调用 MessageQueue 根据时间插入消息
- 在 native 层,向内核空间写入计数器值 ,以此唤醒监听该 fd 的 epoll
说明:epoll 是一种阻塞的 IO 多路复用机制,它可以使用一个文件描述符管理多个文件描述符(fd)。在Android用epoll 机制来管理 native 层的事件。后面细说。
小结:
- Handler 作为一个工具类,可以通过 Handler.post/send 系列方法发送消息。之后统一调用 Handler.enqueueMessage() 方法调用当前线程的MessageQueue.enqueue() 将消息入队。
- Handler 的 delay 是通过 SystemClock.uptimeMillis() + delayMillis 得到消息的执行时间,然后根据这个时间先后将 Message 入队到 MessageQueue 。
- Message只能使用一次,不能重复使用,因为使用之后的 Message 会通过msg.markInUse()标记,再次使用就会抛出异常。
- native 层也存在 MessageQueue 和 Looper ,功能跟 Java 的类似,都是用来进行消息通知。
- 当MessageQueue.next() 阻塞后一直等待 pollOnce() ,当有一个新的消息要入队,如果新消息更不是一个同步障碍,并且消息是异步的,就将消息入队,并且通过 MessageQueue::nativeWake() 间接的通知 native 层的 Looper,通过Looper::wake() 通过 eventfd发送信号,唤醒 epoll ,让消息重新轮询起来。
处理消息
Handler 处理消息一般是通过重写 Handler.handleMessage(Message)。 它完全是被动调用的,当消息到来时,会从内核一直通过native 层传递到 Java 的 Looper。
首先我们先看一下,在 Handler 的 java 层 是如何调用 Handler 的 handleMessag() 的,首先我找到了下面的方法,是Handler 自己调用的:
/**
* 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);
}
}
然后我们在看一下消息的是如何取出来的?是通过 Looper.loop() 不停的轮询从MessageQueue获得的,那么我们再看一下 Looper.loop(),由于代码太多,我们省略一下与当前目的无关的代码;
Looper.loop()
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//...省略...
me.mInLoop = true;
final MessageQueue queue = me.mQueue;
//...省略...
for (;;) {
// 获取下一条消息,这里有可能会阻塞
Message msg = queue.next();
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...省略...
try {
// 如果通过 MessageQueue.next() 取出的消息不为空,
// 就通过 Message.target.dispatchMessage() 传递消息
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
//...省略...
} finally {
//...省略...
}
//...省略...
// 回收消息
msg.recycleUnchecked();
}
}
在 Looper.loop() 中通过 MessageQueue.next() 取出 Message 后,会调用 Message.target.dispatchMessage() 传递消息。那么target是谁呢?我们看一下 Message 的代码:
Message
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
/*package*/ int flags;
public long when;
/*package*/ Bundle data;
// 可以看到 target 就是一个 Handler
/*package*/ Handler target;
/*package*/ Runnable callback;
/*package*/ Message next;
//...省略...
}
那么 Message 的 这个 target 是什么时候幅值呢?我们回去看一下 Handler.enqueueMessage() 方法:
enqueueMessage
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 把当前 Handler 的引用赋值给了 Message.target
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在方法的第一行就把当前 Handler 的引用赋值给了 Message.target,这也就是我们使用 Handler.sendMessage() 等方法时,可以通过 Handler.handleMessage() 获取发送的消息的原因。
了解完 Java 层消息的处理方式,那么我们看一下native层的内容。回到上面的 Looper.loop() 方法,这里是消息轮询的地方,通过 MessageQueue.next() 获取下一个消息的时候会产生阻塞,只有在有消息入队时,才会触发。我们具体看一下MessgeQueue.next() 方法,这里会省略掉一部分无关的代码。
MessgeQueue.next() 相关代码:
MessgeQueue.next()
Message next() {
// native 层 NativeMessageQueue 的指针,MessageQueue可以通过该指针获取到对应NativeMessageQueue
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// 死循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 调用native方法,轮询一次,等待消息,直到 nextPollTimeoutMillis 超时
// 这里会阻塞,等待 native 层的消息处理完毕
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;
if (msg != null && msg.target == null) {
// msg.target==null 表示当前msg是一个同步障碍,到这里会止步不前,直到获取到下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 判断获取到消息是需要延迟
if (now < msg.when) {
// 消息还没有到执行时间,设置下次轮询的时间,使用的nativePollOnce的超时时长来设置
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 已使用,并返回 msg
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
// 如果要退出,将 NativeMessageQueue 的指针销毁,返回 null
// Looper.loop() 中获取到空值会退出轮询
dispose();
return null;
}
// 当前没有消息处理,就会处理一些闲置的任务。处理闲置任务仅限于以下两种情况
// 1. 消息队列为空
// 2. 第一个消息还未到执行时间(有可能是一个同步障碍)
// 仅会在第一次闲置才会执行下面的操作,不会执行第二次
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
// 当前没有消息要处理,则执行 IdleHandler 中的任务
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 如果没有 IdleHandler 需要处理,则去继续轮询等待
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 执行 IdleHandlers 里的内容
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置 idle handler 的数量,使其不小于0,以后再也不会执行
pendingIdleHandlerCount = 0;
// 当我们执行 idle handler 的时候,可能有一条新的消息传递进来,
// 回到最初,重新检查消息是否可以执行,把nextPollTimeoutMillis重置为0
nextPollTimeoutMillis = 0;
}
}
上面的代码核心操作在于 nativePollOnce(),在这里等待消息的到来,它是在native层处理。
具体看看 MessageQueue.cpp 的 nativePollOnce() 方法 的代码:
MessageQueue::nativePollOnce()
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
// 通过 ptr 获取到 NativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
// 调用 NativeMessageQueue 的 pollOnce()
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
MessageQueue.cpp 的 nativePollOnce() 方法调用 NativeMessageQueue 的pollOnce()
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
// NativeMessageQueue 又把工作交给了 native 层 Looper 处理,像极了 java 层
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
NativeMessageQueue 又把工作交给了 native 层 Looper->pollOnce() 处理:
Looper::pollOnce()
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
// 熟悉的死循环
for (;;) {
// 遍历 Response 事件, mResponses 是 Vector类型,类似 java 中的 List
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
// 当 ident == -2 时表示有回调,小于0;所以这里优先处理没有回调的 response
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != nullptr) *outFd = fd;
if (outEvents != nullptr) *outEvents = events;
if (outData != nullptr) *outData = data;
return ident;
}
}
// result 始终等于0,if 语句不执行,下面继续轮询
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != nullptr) *outFd = 0;
if (outEvents != nullptr) *outEvents = 0;
if (outData != nullptr) *outData = nullptr;
return result;
}
// 再续轮询内部事件
result = pollInner(timeoutMillis);
}
}
再继续看 Looper::pollInner() 代码较多,这里删除了一部分 log:
Looper::pollInner()
int Looper::pollInner(int timeoutMillis) {
// 根据下一条消息的到期时间调整超时。。。省略
// Poll.
int result = POLL_WAKE;
// 清空所有的 response
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
// 初始化 epoll_event,最多处理 EPOLL_MAX_EVENTS=16 个事件
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 调用 kernel 层的 epoll_wait,等待 epoll 监听的文件描述符上的哦事件,最多,监听 EPOLL_MAX_EVENTS 个
// 返回的是需要处理的事件数量,如果超过0表示超时
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mPolling = false;
// Acquire lock.
mLock.lock();
// Rebuild epoll set if needed.
// 重建 epoll
// 只有在新加入或移除文件描述符时才有可能触发,这里不用考虑
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
// 处理事件
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
// 处理之前 eventfd 发来的唤醒队列轮询的事件
if (fd == mWakeEventFd.get()) {
// EPOLLIN = 1 , 只要 eventfd 发来的数据大于0 就会执行唤醒操作
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
// 根据监听的 epoll 事件的类型,判断事件的类型
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
// 将事件类型和 request 相关内容放到 response 列表中,供以后获取
// 这里的 mRequests 相当于 java 中的 Map,以index为key,request为value
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// 处理 native 层消息的 Message
mNextMessageUptime = LLONG_MAX;
// mMessageEnvelopes 是 native 层发送的消息列表,在 Looper::sendMessageAtTime()中添加新的消息
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// 获取 native 层发送的消息
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
// 消息执行时间到了,通过其 handler 处理消息
if (messageEnvelope.uptime <= now) {
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
// 通过 native 层的 handler 处理消息,可以看出 Looper 优先处理native层消息
handler->handleMessage(message);
} // release handler
mLock.lock();
mSendingMessage = false;
// 执行了回调
result = POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// 处理所有的带有回调的 response
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
// 处理请求的回调,这里需要注意,回调有可能会关闭掉文件描述符,所以在执行完回调后,需要移除
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
// 清除文件描述符
removeFd(fd, response.request.seq);
}
// 清空 response 引用的所有回调方法
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
// 这里返回了,就代表 java 层就可以轮询一次了
return result;
}
Looper::awoken() 和 Looper::pushResponse()
简单看一下Looper::awoken():
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
uint64_t counter;
// 调用 eventfd 的 read 读取唤醒的信号,当线程在接收到信号之后,就会从睡眠中转为 runnig 状态
TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}
下面是 Looper::pushResponse() :
void Looper::pushResponse(int events, const Request& request) {
Response response;
response.events = events;
response.request = request;
mResponses.push(response);
}
暂时小结一下:
- 在 java 层获取消息时会通过 Looper.loop() 不停的轮询从 MessageQueue 中获取消息;
- 从 MessageQueue 中获取消息的方法是 MessageQueue.next() 方法,该方法有可能会阻塞,等待 native 层的消息处理完毕;
- 在 native 层也有同样有类似的消息传递,通过 Request 和 Response 的方式来完成消息的处理;
- Android 会优先处理 native 层的消息,当处理完 native 层的消息,才会打开阻塞让 java 层处理消息;
- native 层使用了 kernel 层的 epoll 机制配合 eventfd 来处理消息。
下面开始介绍 epoll。
epoll 机制
epoll 是 Linux 上的一种 IO 多路复用机制。在没有 epoll 之前,Linux上使用的是 select 和 poll 的 IO 多路复用机制。这两种方式有以下缺点:
- select 、poll 机制中,单个进程能够监控的文件描述符不得超过进程可打开的文件个数的上限,默认为 1024。即使修改,也会回到性能问题;
- select、poll 轮询效率会随着监控的文件描述符的数量的增加降低;
- select 、poll 从内核空间返回到用户空间的是整个文件描述符数组,程序还需要额外在遍历整个数组才能知道那里写文件描述符出发了相应的事件。
epoll 是在Linux 内核2.6中提出的,是 select 和 poll 的增强版。epoll 更加灵活,没有文件描述符的限制。epoll 使用一个文件描述符管理多个文件描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样用户空间和内核空间之间只需要 copy 一次。这就是著名的 mmap ,关于 mmap 以后再介绍,感兴趣的可以参看 认真分析mmap:是什么 为什么 怎么用
epoll 操作过程中主要需要三个接口:
int epoll_create(int __size);
int epoll_ctl(int __epoll_fd, int __op, int __fd, struct epoll_event* __event);
int epoll_wait(int __epoll_fd, struct epoll_event* __events, int __event_count, int __timeout_ms);
在 Android 升级过程中,在 api 21 和 28 又添加了以下的三个接口:
int epoll_create1(int __flags) __INTRODUCED_IN(21);
int epoll_pwait(int __epoll_fd, struct epoll_event* __events, int __event_count, int __timeout_ms, const sigset_t* __mask) __INTRODUCED_IN(21);
int epoll_pwait64(int __epoll_fd, struct epoll_event* __events, int __event_count, int __timeout_ms, const sigset64_t* __mask) __INTRODUCED_IN(28);
不过 epoll 的核心功能不变,到最后都会调用到上面的三个接口。我们主要介绍先期的三个接口。
- int epoll_create(int __size);
创建一个 epoll 文件描述符句柄, size 是指定内核需要监听的文件描述符的数量。这个 size 不是限制 epoll 所监听的描述符的最大个数,他只是对内核初始分配内部数据结构的一个建议。
当创建好 epoll 句柄后,它就会占用一个文件描述符,在使用完 epoll 后必须通过 close() 关闭,否则文件描述符可能会被耗尽。 - **int epoll_ctl(int epoll_fd, int op, int fd, struct epoll_event_ event)_
对指定的文件描述符进行 op 操作,即事件的 增,删,改。- epoll_fd: epoll_create 返回的句柄
- op: 操作类型,有
EPOLL_CTL_ADD(添加)
,EPOLL_CTL_DE(删除)
,EPOLL_CTL_MOD()(修改)
三种操作码 - fd:需要监听的文件描述符
- event:一个叫做 epoll_event 的结构体,用来告诉内核监听的事件内容,具体代码如下 epoll_event.h :
#pragma once
#include <sys/cdefs.h>
#include <stdint.h>
/** The union of possible data types for an `epoll_event`. */
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
/** The type representing an epoll() event. */
struct epoll_event {
uint32_t events;
epoll_data_t data;
}
#ifdef __x86_64__
__packed
#endif
- **int epoll_wait(int epoll_fd, struct epoll_event_ events, int event_count, int timeout_ms);_
等待 epoll_fd 上的 IO 事件,最多返回 event_count 。- epoll_fd:epoll_create 返回的句柄
- events:监控的事件的集合
- event_count: 内核监控的事件有多大,它不能大于 在 epoll_create() 中的 size
- timeout_ms: 超时时间,单位毫秒。如果传入 负数 表示不确定,如果 events 的数量为0 并且 timeout 还有剩余,或者为负数,则会去查询已经就绪的事件列表。
说明:
当进程调用 epoll_create() 方法时, 内核就会创一个 eventpoll 的结构体,这个eventpoll 结构体里会维护一个红黑树,当调用 epoll_ctl() 时会创建一个双向链表,并将新增加的文件描述符添加到 eventpoll 结构体中的红黑树上,然后向内核注册有事件到来的回调h函数,当设备上的事件来临时,回调函数会向双向链表中插入就绪的文件描述符,并唤醒 epoll_wait 进程。当执行 epoll_wait() 就会立即返回链表中的数据。
epoll 小结
epoll 通过 epoll_ctl() 注册一个文件描述符,当这个文件描述符就绪时,内核会采用回调的方式,迅速激活文件描述符,当进程调用 epoll_wait() 时便会得到通知,epoll 通过每个文件描述符定义的回调函数来响应事件,而不是像 select、poll 一样通过轮询的方式。
另外,epoll 监视的文件描述符数量不受限制,并且IO 效率也不会随着监视的文件描述符数量的增长而下降。
好了 epoll 就简单介绍到这里了,更具体的内容请参看 Linux IO模式及 select、poll、epoll详解 和 Linux 2.6 中 epoll 相关的源码
继续处理消息
回到上面的 Looper::pollInner() 和 Looper::awoken() 中
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 调用 kernel 层的 epoll_wait,等待 epoll 监听的文件描述符上的哦事件,最多,监听 EPOLL_MAX_EVENTS 个
// 返回的是需要处理的事件数量,如果超过0表示超时
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
在这里监听的 mWakeEventFd 和 mEpollFd 这个问价描述符,是在哪里通过 epoll_ctl 注册的呢?
首先看Looper的构造函数:
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(0),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
// 重置 mWakeEventFd 的值
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();
}
继续看 Looper::rebuildEpollLocked():
Looper::rebuildEpollLocked()
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
// epoll 已经创建过句柄了, 重置 mEpollFd
mEpollFd.reset();
}
// 通过 epoll_create1 创建 epoll 句柄
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
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.get();
// 将唤醒系统的 mWakeEventFd 加入 epoll 的描述符表中
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
// 将 request 列表中的事件加入 epoll 的描述符表中
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
这样就将唤醒和request的事件加入到 epoll 监听的事件列表中。当通过 eventfd 的 read 或者 epoll 的 epoll_wait 时就会得到通知。
小结:
- 在native 层也是通过 Handler 和 Looper 的形式进行消息传递;
- native 层通过 epoll 和 eventfd 进行线程的唤醒;
- native 层构建 Looper 时就会创建 epoll 文件描述符,并将唤醒的文件描述符加入到epoll事件中;
- epoll 的优点在于 mmap 和 事件的结构。mmap 减少一次用户空间和内核空间之间只需要 copy 一次,而通过红黑树和链表使事件查询更便利
通过上面的描述,我们丰富一下 Handler 机制的原理图:
总结
- Android 在应用启动时,会在 ActivityThread 被创建时会初始化Looper,这也是为什么 UI 线程默认就可以使用 Looper原因;
- Looper.loop() 是一个死循环,那为什么不会导致应用卡死呢?虽然当没有消息时会线程会自动休眠。并且应用的卡死是因为主线程的操作在 onCreate/onStart/onResume 等回调中的操作耗时过长,而导致 ANR 。Looper.loop() 不会导致应用卡死;
- 当 Handler 发送消息后,会在 java 层将消息入队到 MessageQueue,如果线程已经休眠就会通过 eventfd 和 epoll的方式唤醒;
- 在 native 层也有一套 Handler/Looper/MessageQueue 的消息机制,并且 Java 层的获取消息时,必须等到 native 层的消息处理完毕之后才会将阻塞打开。