个人笔记
Android — Handler常见问题
Handler 是Android所提供的一套消息处理机制,通过Handler、Looper、MessageQueue、Message构建出一套完整机制
一.Handler消息处理流程
1.Handler 发送、处理消息
首先可以看一下Handler的初始化
public Handler(@Nullable Callback callback, boolean async) {
...
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;
...
}
可以看到Handler的初始化其实做了两件比较重要的事
- 通过Looper的myLooper()方法获取了Looper的实例,当Looper实例为空时会抛出异常
- 从Looper中获取消息队列
接下来看一下Handler发送消息的常用方法 sendMessage(Message msg)该方法在底层最终会调用sendMessageAtTime(Message msg, long uptimeMillis)
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);
}
在该方法中,调用enqueueMessage()将Message加入MessageQueue
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
该方法中将Handler赋值给msg.target进行绑定,最终将Message加入MessageQueue
post(Runnable r)该方法与sendMessage()方法相同,最终都是调用enqueueMessage()将Message加入到MessageQueue中
唯一的不同是Handler会通过调用getPostMessage(Runnable r),将Runnable封装成一个Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
看完消息发送,接下来看一下Handler如何处理消息
首先Handler会通过调用dispatchMessage()方法来分配消息
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 1
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; // 2
}
}
handleMessage(msg); // 3
}
}
可以看到消息分发时有三种处理方式
msg.callback在调用post(Runnable r)方法传递消息时,可以看到传入的Runnable会被封装到Message中,则会走逻辑1,此时会直接执行该Runnable
private static void handleCallback(Message message) {
message.callback.run();
}
mCallback在Handler初始化时,能够传入一个callback直接重写handleMessage()方法
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
并且会赋值给mCallback,此时会进入逻辑2,直接调用重写的handleMessage()来处理消息
handleMessgae()最后一种,创建一个继承Handler的子类,并重写其中的handleMessage()方法,这时则会进入逻辑3,调用子类的handleMessage()来处理消息
2.Looper 消息流转
看过了Hanlder是如何发送和处理消息的,接下来看一下消息内部流转的具体细节
首先就是在handler初始化时获取的Looper实例
初始化时通过调用Looper.myLooper()获取Looper实例
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
该方法就是取出存入ThreadLocal中的Looper实例
但该方法只是取出Looper实例,那Looper对象的创建具体是在哪呢
可以看到在Handler初始化获取looper对象为空时,会抛出异常,而该异常就是提示如果Looper为空则应先调用Looper.prepare()方法
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对象的创建是在该方法中,并且被存入ThreadLocal中,并且重复调用prepare()方法同样会抛出异常,所以一个线程中只会拥有一个Looper
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的初始化中,会创建一个新的MessageQueue对象
可在日常主线程中使用Handler时,其实并没有调用prepare()方法,那为什么没有抛出异常呢
其实在ActivityThread中,即主线程的main()方法中其实已经为我们处理了Looper的初始化
public static void main(String[] args) {
...
Looper.prepareMainLooper(); // 1
...
Looper.loop(); // 2
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看见main()方法中与Looper相关的
- 首先是
prepareMainLooper()方法,它与prepare()相同,为我们在主线程中创建了一个Looper对象,所以日常在主线程中使用Handler时,并不需要调用Looper.prepare()来初始化Looper - 其次就是
Looer.loop()方法
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue; // 1
for (;;) {
Message msg = queue.next(); // might block // 2
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg); // 3
...
} catch (Exception exception) {
...
} finally {
...
}
...
}
loop()方法是消息处理机制中最重要的部分之一
- 首先它会获取Looper中对应的
MessageQueue - 其次通过一个
死循环从MessageQueue中取出Message。而取出消息是通过MessageQueue.next()该方法有可能会造成阻塞 - 在前文Handler部分可以看到在发送消息时,会将对应的Handler实例赋值给Message的
target,而这时取到消息后就会调用对应Handler的dispatchMessage()方法来分发消息,走到Handler消息处理的部分
这样就是一个完整的消息流转处理的一个过程
handler.sendMessage() -> messageQueue.enqueueMessage() -> looper.loop() -> queue.next() -> msg.target.dispatchMessage() -> handler.handleMessage()
3.MessageQueue 添加、取出消息
上述提到Handler发送消息最终会将Message加入到MessageQueue中,因此我们来具体看一下MessageQueue.enqueueMessage()方法
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { // 1
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) { // 2
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) { // 3
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) { // 4.1
// 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 (;;) { // 4.2
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;
}
- 可以看到能够加入到MessageQueue中的Message必须与一个对应的
traget绑定,即与一个对应的Handler绑定 - 当前的Message如果正在被使用,则也不会加入到queue中
- 若当前线程已经退出,则Message同样也不会加入到queue中
- 加入MessageQueue中有两种情况
- 消息队列中没有消息或该消息是一个即时的消息,则此时的消息将会插入到队列头部,并且如果此时Looper中的线程被阻塞,同时还会唤醒该线程
- 若此时消息是一个延时消息,则会通过比较延时的时间循环加入到消息队列中,获得一个
有序的延时消息链表
Looper.loop()方法中,最终取出消息是通过MessageQueue.next()方法
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
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();
}
nativePollOnce(ptr, nextPollTimeoutMillis); // 1
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) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) { // 2
// 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 { // 3
// 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.markInUse();
return msg;
}
} else { // 4
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
...
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
- 可以看到
nativePollOnce()是一个阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的
- 如果
nextPollTimeoutMillis=-1,一直阻塞不会超时 - 如果
nextPollTimeoutMillis=0,不会阻塞,立即返回 - 如果
nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回
- 此时如果还没到Message的发送时间,则会将
nextPollTimeoutMillis设置为消息发送需要等待时间,并在下一次循环时nativePollOnce()来阻塞线程 - 正常取出消息,将mBlock设置为false,表示现在线程没有被阻塞,并将message标记为正在使用中
- 当没有消息时,将
nextPollTimeoutMillis设置为-1,来阻塞线程