Handler是什么
概述
Android Framework提供的一个基础组件,用于线程间通信。主要是子线程UI更细消息传递给主线程,从而主线程更新UI。
Android 主线程的UI,只能主线程更新。 如果多个线程都能更新,势必要「加锁」,还不如采用「单线程消息队列机制」
主线程内部维护一个循环。没有消息时候,这个循环是阻塞的。新来消息(或者阻塞timeout)时会唤醒,接着处理新到来消息。
Java层Handler
整个handler可以粗略的分为以下:
- 等待读消息阶段: Prepare Looper与MessageQueue阻塞轮询
- 写消息阶段: Message封装、入队列
- 处理消息阶段: Message分发处理
1.Prepare Looper与MessageQueue阻塞轮询
使用HandlerThread构建Handler为例,
val handlerThread = HandlerThread("name-xx")
handlerThread.start()
var threadHandler = Handler(handlerThread.looper)
分析HandlerThread内部实现,发现当调用HandlerThread.start()随即调用run(),内部几乎都是对Looper的使用。
public class HandlerThread extends Thread {
Looper mLooper;
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
public Looper getLooper() {
synchronized (this) {
while (mLooper == null) { wait(); }
}
return mLooper;
}
1.1 Looper.prepare()
基于MessageQueue的 event loop,存储Looper到当前线程局部变量
public final class Looper {
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
public static @Nullable Looper myLooper() {return sThreadLocal.get();}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
}
//初始化,Thread改造为Looper可用。
//loop()方法前必须调用prepare()
public static void prepare() {
prepare(true);
}
//这里要注意,非主线程的Looper是允许退出的,而主线程的Looper是不允许退出的
private static void prepare(boolean quitAllowed) {
//不管怎样ThreadLocal的Looper对象只能初始化一次,所以prepare只能调用一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//不允许退出,要不然主线程的事件得不到处理,就要ANR了
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
1.2 Looper.loop()
消息的逻辑还是在MessageQueue中
public final class Looper {
/**
* 在线程中运行消息队列
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
// 1.阻塞,直到next有返回值。
Message msg = queue.next();
//2.后面提到的消息分发处理
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
}
题外话:主线程的Looper是在哪启动的?
//管理进程主线程的各种操作
public final class ActivityThread {
//这个方法是应用进程的入口方法,应用进程创建后立即运行。
public static void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop();
}
}
1.3 MessageQueue.next()
就堵塞在这了nativePollOnce(ptr, nextPollTimeoutMillis)。
此处没有用java中的wait/notify堵塞,而是通过Linux的epoll机制来堵塞,原因是需要处理 native侧 的事件。
没有消息时堵塞并进入休眠释放CPU资源,有消息时再唤醒线程。
epoll参考:epoll的本质
class MessageQueue {
private long mPtr; // used by native code
Message next() {
/**等于0时,不堵塞,立即返回;
*大于0时,最长堵塞等待时间,期间有新消息进来,可能会了立即返回(立即执行);
*等于-1时,无消息时,会一直堵塞;
*/
int nextPollTimeoutMillis = 0;
for (;;) {
//阻塞在此,超时或者被唤醒时,执行接下来的代码
//ptr是啥?实际指向NativeMessageQueue对象
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
//我们发现拿到的第一个消息,执行时间还没到,那我们计算下超时时间,用于下一次的nativePollOnce
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.提取这个消息,并以下一个消息重新作为消息头
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
}
}
}
}
2.Message封装、入队列
我们先来看发送消息,这是我们最简单的一种发送消息的方式。
//构建一个处理message的handler
val handler = object : Handler() {
override fun dispatchMessage(msg: Message) {
Log.d("alvin", "msg.what:${msg.what}")
}
}
var msg = Message().also { it.what = 1 }
handler.sendMessageDelayed(msg,1000L)
稍稍追踪下代码我们发现,有很多sendMessage方式,都统一到一个方法中即
//Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//这个分发用的,省略了一下其他代码
return queue.enqueueMessage(msg, uptimeMillis);
}
接着调用到MessageQueue.enqueueMessage(Message msg, long when),代码在下面,
解释下,我们有一个单链表message,message有next指向下一个message,以及此message触发时间when。单链表是有顺序的,when越小,就越靠前。 根据when,新message插入合适位置,如果恰好插在队首,则需要唤醒nativeWake。所以归结下来两件事情:入队和唤醒。
public final class MessageQueue {
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
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 {
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) break;
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) nativeWake(mPtr);
}
return true;
}
}
3. Message分发处理
在Looper.loop()中执行了msg.target.dispatchMessage(msg).
msg.target就是我们的Handler对象。早在Handler.enqueueMessage(msg,...)时,我们执行了msg.target = this;
这里要区分下 post(message)和 sendMessage的不同处理方式
class Handler{
public void dispatchMessage(@NonNull Message msg) {
//msg.callback跟踪下代码。哦,原来是post(Runnable),时候构建一个带callback的message
if (msg.callback != null) {
handleCallback(msg);//-->message.callback.run();
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//我们并没有给handler设置mCallback,所以执行handleMessage
handleMessage(msg);
}
}
//post(Runnable),时候构建一个带callback的message
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
}
4.Java层 其他
关于同步屏障SyncBarrier如下:
- Message分为3种:同步消息、异步消息、同步屏障消息。
- 同步屏障消息也是一种消息,可以插入到
MessageQueue中,位置同样取决于Message.when。区别于不同消息,同步屏障消息没有target,因为不用处理回调。 - 同步屏障消息作用为:
-
对于when小于同步屏障消息的消息,处理方式不变
-
对于when大于同步屏障消息的消息,优先选取异步消息处理,如果此异步消息时间未到,则继续轮询等这个异步消息。
-
虽然主流程没有了问题,但我们省略了很多代码,可以作为问题来探索
- Looper.loop() 能退出么,如果一直没有消息的话,阻塞在那,岂不是会anr?
- Message.obtain(),消息缓存池
- Handler.removeCallbacksAndMessages()
Native层Handler
消息轮询机制是通过MessageQueue.nativePollOnce()阻塞和通过MessageQueue.nativeWake等方式唤醒。
MessageQueue通过mPtr变量保存NativeMessageQueue对象,从而使得MessageQueue成为Java层和Native层的枢纽,既能处理上层消息,也能处理native层消息;下面列举Java层与Native层的对应图。
要注意的是Java层和C++层分别实现了一套Handler机制代码。
Looper.prepare()
Java层:new Looper()-->new MessageQueue()-->mPtr = MessageQueue.nativeInit() -->native层:android_os_MessageQueue_nativeInit()-->new NativeMessageQueue()-->new Looper(false) 最终走到了Looper.cpp的构造方法
Looper::Looper(bool allowNonCallbacks)
rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;//监听mWakeEventFd有写入数据。
eventItem.data.fd = mWakeEventFd.get();
//创建 eventpoll 对象,返回一个 epfd,即 eventpoll 句柄。
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
}
要说 epoll_ctl()干嘛用的,我也不太会了, 可以参考。
Looper.loop()
从Java层分析,一顿操作进入到了Looper::pollInner()
Looper.loop()
-->MessageQueue.nativePollOnce(mPtr,timeout)
进入native层:
android_os_MessageQueue_nativePollOnce()
-->NativeMessageQueue::pollOnce()
-->Looper::pollOnce()
-->Looper::pollInner()
//platform/system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
//1.timeoutMillis处理,省略...
//2.开启等待,监听mWakeEventFd写入事件,省略...
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//Looper::wake()后执行到这。
//3.后面等唤醒后执行,先执行native层添加的message,逻辑与java层处理消息类似
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
handler->handleMessage(message);
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
//4.走回java层,MessageQueue.nativePollOnce(),执行java层的消息提取。
}
Handler.sendMessage()
根据前面Java层分析,一顿操作进入Looper::wake()
MessageQueue.nativeWake()
进入native层:
android_os_MessageQueue_nativeWake()
-->NativeMessageQueue::wake()
-->Looper::wake()
//platform/system/core/libutils/Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
//往mWakeEventFd里面写了1,随后进入到Looper::pollInner(),唤起”epoll_wait“,继续执行。
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
}
Native层总结
与Java层类似,但同时我们省略了很多代码,带着问题继续看代码
- Looper不仅仅用来处理Handler,还有其他作用,看看MessageQueue.addOnFileDescriptorEventListener代码
- ALooper.cpp是啥