文章目录
基本使用
Handler handler = new Handler();
Message message = handler.obtainMessage(); // 或者 Message message = Message.obtain()
handler.send(message); // 基本
handler.post(new Runable() {
@Overriable
public void run() {
// do ...
}
})
源码流程
Handler 机制主要涉及四个主要部分:
- Handler 负责消息收发处理代理,或者处理代理设置
- Message 消息结构内容,如果为Runnable,最终也将转化为 Message.callback,另外Message提供了资源池,提供于反复利用。
- Looper 负责便利消息队列(MessageQueue.next())进行便利,并且根据对应的Handler进行分发(Message.target)
- MessageQueue
通常我们通过 Handler 就可以实现大部分功能的使用,在Handler 中,同时封装了 mLooper
和 mQueue
,而 mQueue
来源于 mLooper
。
public Handler(@Nullable Callback callback, boolean async) {
...
// 从当前线程中获得 Looper, 注意如果当前线程非主线程,则需要在当前线程提前使用 Looper.prepare() 以产生 Looper 对象,否则将报错 Only one Looper may be created per thread
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 获取 MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
post
分析代码通常会从使用上入手,这里我们从简单的Handler.post(Runnable r)
开始分析。
Handler.post(Runnable r)
post 主要是对sendMessage 的参数包装方法。
通过getPostMessage
对 Runnable
参数进行的包装,将Runnable
参数作为Message.callback
参数,转化为Message
对象,最终又调用enqueueMessage
方法,将Message
作为参数传入。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
包装有 Runnable 参数的 Message,最终进入该方法,并且将Message
添加到 MessageQueue
中(MessageQueue.enqueueMessage(Message, time)
),至此Message
进入MessageQueue
队列中,等待被调用执行。
// 比较简单,只是为了整合一些内容
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(MessageQueue queue, Message msg,long uptimeMillis)
该方法是将消息添加到 MessageQueue 的链表 mMessages 中,并决定是否激活Looper
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
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; // 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 {
// 主要是为了将消息根据when插入到 消息链合适的位置
// 如果已经被阻塞并且队头是阻塞消息,当前消息是异步的情况 则重新唤醒looper
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;
prev.next = msg;
}
if (needWake) {
// 根据情况去激活Looper
nativeWake(mPtr);
}
}
return true;
}
nativeWake(long ptr)
以下借用下别人的图,对于JNI这块还不太熟悉,简单记录下JNI下发生的内容。
nativeWake 是一个native 方法,映射于 android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr)
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
// 将Java层传递下来的mPtr转换为nativeMessageQueue
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//调用wake函数
nativeMessageQueue->wake();
}
主要也是负责执行 wake()
void NativeMessageQueue::wake() {
mLooper->wake();
}
执行 Looper::wake()
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
// 向管道mWakeEventFd写入字符1
// TEMP_FAILURE_RETRY 是一个宏定义,当执行write失败后,会不断重复执行,直到执行成功为止。
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
到这里为止 Post 的流程结束,而Send流程其实和 Post差不多。
Main Handler
在之前中,我们提到一个Looper是需要在当前线程总调用 Looper.perpare 去产生的。但是在为什么主线程中,不需要调用也能执行。
这涉及到App的启动流程,在一个App启动后,会经过AMS到最后的ActivityThread,在这过程中,会调用ActivityThread.main() 去启动创建一个 ActivityThread 同时创建了Main线程的Looper。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
...
}
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));
}
为什么不会发生ANR
前面解析到如果在一个MessageQueue 中没有消息的话,那么Looper将进入阻塞,那么为什么不会产生ANR呢。
在Android主线程中,MainLooper 承担着UI绘制、触摸事件、键盘事件等一切可以想得到的主线程工作内容,都是通过MainLooper去处理。
在这里面光一个UI刷新则已经让MainLooper每一秒都停不下来(60ms标准),更何况很难再6s内阻塞,并且及时发生了,那么UI一定也卡住了,这已经是明显的ANR了。
Asynchronous 和 SyncBarrier
在我们分析中,应该已经发现了,在Message和Looper 等中会不断出现 Asynchronous 这一属性或者判断。
这里需要结合 SyncBarrier 一起说明,在Handler中消息分为异步消息、同步消息和阻塞消息(普通用户没办法直接创建),一般情况下,我们创建的消息都是同步消息,这里SyncBarrier 的作用主要体现在对同步消息和异步消息的处理态度。
SyncBarrier 也是Message的一种,只是该Message的 target=null(Message的target通常是发送它的Handler),对于该种消息的作用,体现在 MessageQueue 的 next() 中
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
...
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// msg.target == null 说明当前队头消息被设置了SyncBarrier
if (msg != null && msg.target == null) {
// 找到该消息后面的异步消息(msg.isAsynchronous()方法)
do {
// 标识前一个消息
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 这时候得到的msg 或者是异步消息,或者是队头消息(非SyncBarrier消息) 或者是最后一个null消息
if (msg != null) {
if (now < msg.when) {
// 还未到消息执行时间,则阻塞这段时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
// prevMsg不为空,从队列中删除即将执行的msg,但是队头保持不变,即在下一个循环中,队头还是个空消息
// 从这里可以看出,如果被设置了SyncBarrier,只有当SyncBarrier被移除的时候,才会执行队列后面的非异步消息
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 替换队头
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 队列中没有可用消息
nextPollTimeoutMillis = -1;
}
}
}
}
因此可以理解为,如果在消息列表读取中发现有同步阻塞消息,则阻塞下面的在它之后的消息执行(被读取到),除非该消息设置了 Asynchronous。
SyncBarrier的应用
同步阻塞通常用在垂直同步的UI刷新中,在ViewRootImpl.scheduleTraversale()中。
...
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
...
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 设置同步阻塞
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 等待下一个vsync信号会回调mTraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
// 该方法在接收到vsync信号时调用,执行view的measure、layout和draw
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步阻塞
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
这样子的处理是为了在执行UI同步时,将UI刷新优先置为最高优先级,延后其他消息的处理,提高流畅度。
IdleHandler
IdleHandler 用于在 Handler 线程空闲的时候(未接受到需要处理的Message时)进行的额外处理。
public static interface IdleHandler {
// 当消息内队列所有的Message都被执行完或者还有消息但是暂时还不需要执行(定时消息)时,该方法会被调用,当返回true,则IdleHandler会保留在消息队列中,否则执行完后将从队列中移除了
boolean queueIdle();
}
调用
MessageQueue messageQueue = Looper.myQueue();
messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
...
return false;
}
});