导读:
本文打算分2步骤来讲解Handler,首先要有一个整体的流程说明,看看他的从Java 到kernel的完整调用过程,由于Handler还算是代码比较简单,逻辑比较清楚,所以这个过程也是相对清晰。
第二个步骤就是习题时间,作为训练,解答市面上关于Handler比较好的问题。
面向读者:好奇Handler具体实现流程的人,需要寻找Hook点,所以必须了解整个流程的人,准备面试的人。
第一步:Handler机制的整体流程
Handler API的调用就类似拿着一个handler的对象到处去发消息,然后在我们创建的Handler会实现一个接受消息的方法,并在这里写好我们的处理逻辑。
Handler所扮演的角色就类似一个管理类,提供给调用方使用,而他的api主要目的也很明确,就是提供给你发消息和接受处理消息的能力,但这些能力的实现却跟handler没有什么关系。
1.1 首先我们看发消息的流程。
Handler的构造器有很多,分别有三个参数,Looper,Callback, async:Boolean, 具体的意思,我们第二步骤再看,先看跟Looper相关的.
public Handler(Looper looper) {
this(looper, null, false);
}
可以指定Looper,那么不指定的肯定就是当前线程的Looper了,因为Looper使用了TreadLocal来保证每个线程都是唯一性的嘛。
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到Handler上来就保存了Looper和Looper中的MsgQ对象,接着可以发现所有发消息的逻辑最后都会会聚到这个方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
有一个msg.target 用Handler对象自身this赋值了,So,所有的Handler发送的msg都携带着发送者的信息,也就是作为发送方的Handler对象,这个作用后面再说,我们先看主流程。
最后都是走的MsgQ的方法,看来真正的发送逻辑和Looper关系不大,而是全靠MsgQ.
我们继续看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 {
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;
}
}
//p是上面遍历跳出的正确插入位置,在p前面插入msg消息
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
//暂时先忽略needWake
if (needWake) {
//调用native层传入的mPtr指针代表着这个MsgQ的对象,提供给cpp调用
nativeWake(mPtr);
}
}
return true;
}
这个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*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
刚才注释说过了,这个ptr代表着Java层的MsgQ对象,强转一下,最后调用了Looper.cpp的wake方法。
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
ssize_t nWrite;
do {
//这里使用了pipe管道,mWakeWritePipeFd代表管道的写端描述符,可以理解为一个flag
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
至此发送的工作结束,一会儿分析接收消息的流程,小总结一下:
发送消息Java层只跟MsgQ有关,Handler -> MsgQ.java -> MsgQ.cpp -> Looper.cpp -> linux kernel的管道,而MsgQ本质是一个按照时间从前到后排序的单链表,由于MsgQ是Looper的,Looper是线程私有的,所以除了Handler的调用,后面的所有流程都是线程私有的,代表了一个线程。
1.2 我们再看接受消息的流程。
我们都知道再Handler类实现的方法fun handleMessage(Msg)可以接受到消息,但这个消息是从哪里来的呢, 发送的时候最后Looper.cpp调用了Linux 内核的管道,这里需要了解一下pipe和epoll的知识,这是一种多复用监听IO机制,描述一下就是,当管道里有人写入了,会唤醒线程并通知epoll来读数据,如果管道被读光了,而暂时也没有写入,那么读的线程会BLOCK,释放掉CPU资源。那么读到了,kernel就会通知到native层,在通知到Java层,我们反过看源码,从handleMessage来逆向观察,首先寻找handlerMessage是在哪调用的:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这里可以看到,给谁处理有三种方式,如果msg本身携带了callback, 那么交给msg处理,如果handler对象自己有mCallback,那么交给自己的mCallback处理,这两个callback都是在Msg和Handler的构造器中由调用方传入的。如果两个都没有意愿处理消息,才调用Handler自身的handleMsg()方法。
那么再上一层,是谁调用了dispatchMessage呢,答案就是在Looper.java中调用的。
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 (;;) {
//著名的死循环,queue.next获取下一个信息可能会导致阻塞block
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//把取到的消息分发给handler处理,msg.target就是handler,上文我们提到过。
msg.target.dispatchMessage(msg);
} finally {
}
/*把msg对象的引用释放掉,断开与GC root节点的引用链
,为什么不直接置为null呢,是因为msg对象有一个池来维护(单链表实现,非常的简洁优雅),来重复利用实例,所以并不想msg对象被回收掉*/
msg.recycleUnchecked();
}
}
很显然,这个死循环,一直在操作的就是用MsgQ取到next节点,那么怎么才能取到next节点呢?
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
/*之前说过这个ptr是MsgQ的引用指针,提供给native层调用,这个方法很关键,next方法后面的内容作为 Area B,暂时隐藏掉*/
nativePollOnce(ptr, nextPollTimeoutMillis);
...
/*code of Area B */
...
下面展示调用链
MsgQ.java -> MsgQ.cpp -> Looper.cpp -> Lopper.pollInner()
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
//ptr强转为MsgQ
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//调用pollOnce本体
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
//取下一个节点的任务也是交个Looper
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
//死循环调用pollInner
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mIdling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//epoll_wait会等待消息的到来,也就是如果有人通过发消息写入了pipe,这是可以取到消息,别忘了这个函数是被死循环调用的。
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mIdling = false;
// Acquire lock.
mLock.lock();
// Check for poll error.
...
// Check for poll timeout.
...
// 获取到信息后,接下来就是读取了
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
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;
// 插入到response队列中供后续获取
pushResponse(events, mRequests.valueAt(requestIndex));
}
}
}
//Done代码块开始
Done: ;
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
// 处理native层的Message
handler->handleMessage(message);
} // release handler
}
}
// Release lock.
mLock.unlock();
// 调用所有的request的回调.
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;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
this, response.request.callback.get(), fd, events, data);
#endif
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
poll小结
pollInner()方法的处理流程:
- 先调用epoll_wait(),这是阻塞方法,用于等待事件发生或者超时;
- 对于epoll_wait()返回,当且仅当以下3种情况出现:
POLL_ERROR,发生错误,直接跳转到Done;
POLL_TIMEOUT,发生超时,直接跳转到Done;
检测到管道有事件发生,则再根据情况做相应处理:如果是管道读端产生事件,则直接读取管道的数据;如果是其他事件,则处理request,生成对应的reponse对象,push到reponse数组; - 进入Done标记位的代码段:
先处理Native的Message,调用Native 的Handler来处理该Message;
再处理Response数组,POLL_CALLBACK类型的事件;
从上面的流程,可以发现对于Request先收集,一并放入reponse数组,而不是马上执行。真正在Done开始执行的时候,是先处理native Message,再处理Request,说明native Message的优先级高于Request请求的优先级。
另外pollOnce()方法中,先处理Response数组中不带Callback的事件,再调用了pollInner()方法。
拿到msg后,我们来看刚才隐藏掉的Area B
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
//Area B 开始
synchronized (this) {
// 查询记录当前的时间 now
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//经过nativePollOnce,取到的msg已经在链表里了
Message msg = mMessages;
//上文我们分析过,handler 发送msg的时候都会把自己赋值给target,这里是异步屏障的处理,一会儿讲
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) {
/*如果msg的时间大于现在的时间,也就是msg不是之前发出的消息*/
if (now < msg.when) {
// 证明这条消息是下一条消息,还没准备好,设置一个超时时间,当他准备好的时候唤醒
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取到消息.
mBlocked = false;
//这里处理异步屏障,会从链表中间截取优先级高的消息
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
//正常消息取第一个也就是msg,所以头节点移到下一个节点
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
//如果这里返回了当然循环就进入下一个,没有返回的话,就会Block
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
1.3 异步屏障
我们再看一下异步屏障这块代码
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());
}
如果target == null,开始遍历单链表,只要msg不是null且msg不是isAsynchronous的,就一直找,这明显是要找到一个消息是isAsynchronous的才会罢休,否则一直往后找。
之前分析过,handler发消息最后会调用enqueueMessage的方法,里面第一行就是msg.target = this;
,那么怎么才能发target为空的消息呢,可以看到越过Handler,MsgQ里面有一个public的方法:
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
//创造一个msg,注意没有复制msg.target,默认值是null
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//插入队列
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
//返回一个令牌token,有点像引用计数
return token;
}
}
//根据token移除这条target为null的消息
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
所以异步屏障就是一条target为null的msg,如果队列中插入了这条msg,那么取到它的时候,整个队列都会暂停,去遍历寻找Mssage.isAsynchronous() == true
的消息,优先取出这条消息返回,然后根据插入屏障消息时候返回的token调用removeSyncBarrier,把这条消息移除,队列回复原来的逻辑。
大概的意思就是全部交通管制,让领导的车先走,领导车过去了,在取消管制,恢复正常交通,一定是因为这条消息比较重要呗。
举一个例子,ViewRootImpl会调用scheduleTraversals()
方法来更新视图,
void scheduleTraversals() {
//插入异步屏障,开始交通管制,并且获取本次管制编号mTraversalBarrier
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//发送重要的更新视图消息,让领导车队先走
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
postCallback最后会调用在这里
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
可以看到这条加急信息是设置了msg.setAsynchronous(true)
,他就可以在异步屏障中畅通无阻,会优先把这条消息取出来执行。
而对应的,领导的车过去了,交通管制结束,拿着刚才的管制编号mTraversalBarrier把屏障移除:
void unscheduleTraversals() {
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
}
第二步:习题
1,Looper 使用死循环for(;;)取消息,为什么主线程没有阻塞?
这个题玩了文字游戏,让你默认为没有阻塞,阻塞肯定是阻塞的,人家源码都写的清清楚楚,会阻塞。但为什么主线程我们感觉不到阻塞,真的只是我们感觉不到而已,因为阻塞的条件是没有消息了,而主线程会受到包括屏幕刷新,视图刷新,Activity生命周期等等各种各样的消息,所以不会感觉到阻塞,这个问题应该问的是为什么没有ANR,下面给出官方文档ANR说明的图:
第四条写的,线程处于阻塞状态,可能导致ANR,所以这个题,可以回答为什么正常情况下是不会ANR的, 而什么情况下才会导致ANR。
2,一个线程有几个 Handler?一个线程有几个 Looper?如何保证?如何在子线程创建Looper?
这个题目也很坑爹, 线程和Handler不存在拥有的关系,每个线程都可以自己new一个handler,或者使用自己工作内存里的主存handler实例的副本,Handler我们分析过了,他只不过是一个管理类,所以和一个普通对象没有什么区别。
Looper我们来看下源码
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
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.set(new Looper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void loop() {...
mQueue = new MessageQueue(quitAllowed);
}
}
每个Looper创建的时候都必须调用Looper.prepare()方法(主线程Looper系统帮忙调用了),为什么呢,因为prepare方法里实例化了一个Looper对象,并且用ThreadLocal存储了他,以后再调用Looper.myLooper的时候,就直接从ThreadLocal里取,这时候在哪个线程调用Looper.myLooper,就会取出所在线程的Looper对象,所以这个题问的也不准确,线程有几个Looper。。。线程没有Looper,是在一个线程实例化Looper的时候(Looper.prepare()),后面在取出来就只能取到这个线程当年创造的这个Looper实例,可以看到这几个方法都是静态方法,这几个属性也都是静态属性,静态属性的引用是存在于虚拟机的方法区的,而且是GC root节点,所以不会被回收,才能保证在任何线程内,调用Looper的这些静态方法,都可以实现功能,如果问一个线程内能得到几个MsgQ,肯定也是一个,因为MsgQ是在Looper中实例化的。
2,Handler导致内存泄露是怎么回事?
这个是因为Handler持有了Aty的引用,用于收到消息时候操作Aty.xxx()
Handler如果作为内部类,直接持有外部类Aty的引用,如果作为独立的类,把Aty作为强引用传入,也是持有Aty的引用。当Aty destroy的时候,Handler还在发消息,接受消息,而此时handler的引用在msg手中
Looper.loop(){
for(;;){
//我们知道 msg.target is handler
msg.target.dispatchMessage(msg);
}
}
loop方法在执行,也就是在虚拟机栈中作为一个栈帧存在着,虚拟机栈的引用也是GC root节点,所以无法回收,而Aty的引用是强引用被Handler持有,就间接连入Gc Root 的引用链了。所以无法回收Aty且已经destroy,导致内存泄露。解决方式使用弱引用即可,当Activity被destroy之后,持有者被置空,调用者被虚拟机栈出栈,脱离GCroot引用链,而与这边的引用是弱引用,下一次GC的时候内存就会被回收掉。