文字总感觉很难描述,还不如来一张图总结一下来得清晰。
Handler#dispatchMessage,消息分发的核心代码
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
1.Looper.prepare()
创建Looper,并保存到sThreadLocal中,创建了一个MessageQueue并赋值给Looper#mQueue,同时在Native层页创建了MessageQueue
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//prepare只要就是创建Looper对象,放在当前线程的ThreadLocalMap中,待后续Looper.loop()从线程中取出来
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //消息队列,构造中调用了nativeInit(),这里会在native层也创建消息队列
mThread = Thread.currentThread();
}
# MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit(); //调用nativeInit,这里会创建native层的消息队列
}
2.创建Handler
从ThreadLocal中获取线程的Looper赋值给Handler的mLooper,Looper的mQueue赋值给Handler的mQueue,所以Handler和其关联的Looper共同持有同一个MessageQueue。
public Handler(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(); //Looper赋值给Handler#mLooper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //Looper#mQueue赋值给Handler#mQueue
mCallback = callback;
mAsynchronous = async;
}
3.Looper.loop()开启消息循环
进入一个for(;;)
循环,mQueue.next()取出消息后,交给Handler#dispatchMessage()来处理,当取出的Message为null时退出循环。以下为关键性源码。
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
//MessageQueue的next方法取出的消息为null,退出循环
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//取出消息后交给Handler的dispatchMessage来处理
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
从注释可以看到,MessageQueue#next()有可能导致线程阻塞,实际上是next()方法中的nativePollOnce()方法会阻塞线程。当处理完成一个Message后调用msg.recycleUnchecked()来回收消息,回收的消息将被缓存到sPool单链表的表头,最大缓存消息数量是50。当调用Handler#obtainMessage()
时,就会从这个缓存链表中获取一个Message对象。
4.对外提供的消息回收接口:Message#recycle()
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
5.MessageQueue
(1)MessageQueue 按Message.when降序排列,在前面的消息先出队(MessageQueue#enqueueMessage、MessageQueue#next)
(2)barrier的Message与普通Message的差别是target(类型是Handler)为null,只能通过MessageQueue#postSyncBarrier创建 barrier Message
(3)barrier的Message与普通Message以同样的规则进入队列,但是却只能通过MessageQueue#removeSyncBarrier出栈
(4)每个barrier使用独立的token(记录在Message#arg1)进行区分
(5)所有的同步消息(相对与异步消息而言,默认消息都是同步消息)如果barrier之后,都会被延后执行,直到调用MessageQueue#removeSyncBarrier通过其token将该barrier清除,当barrier在队头时,队列中的异步消息照常出队不受影响
(6)Handler中的对应构造函数被隐藏,但是可以通过调用Message#setAsynchronous指定对应的Message为asynchronous的Message。
(7)部署barrier(MessageQueue#postSyncBarrier)与清除barrier(MessageQueue#removeSyncBarrier)的相关方法都是隐藏的方法,对外不可见
6.使用Handler值得注意的事儿
(1)引起内存泄漏问题
public Handler(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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
异常:The following Handler class should be static or leaks might occur.
原因:
由于Handler有可能(绝大部分)会被投递到MessageQueue中的Message#target所引用,在此消息没有被处理的情况下将一直持有,此时非静态内部类的Handler持有外部类实例的引用,例如是一个Activity实例,如果此时Activity退出,将会由于被Handler强引用而无法及时GC,导致内存泄漏。
通常处理方法:
①将Handler改为静态内部类,使用弱引用来应用外部实例,只有若弱引用的对象,在GC时可以回收。关于Java中的四种引用,可以看看这篇文章《Java中的4种引用类型》。
②在外部类实例销毁时,调用Handler#removeMessage()将消息从消息队列中移除回收掉,这样就能移除Message对Handler的引用,当外部实例销毁时,Handler变成可被GC的对象。
(2)创建Handler时,提示当前线程没有Looper
Can't create handler inside thread that has not called Looper.prepare()
原因:
Looper.prepare()实际上是创建一个Looper传入作为所在线程的局部变量(全局由ThreadLocal与Thread#localValues来保证,简单参考ThreadLocal#get、ThreadLocal#set即可理解),而在真正Looper#loop的时候,是需要所在线程的局部变量的Looper为载体取得所有要处理的消息以及处理的方式的。因此创建Handler的同时是需要保证所在线程已经有了局部变量Looper的实例,才能保证Handler接下来真正运作。
通常处理方法:
在创建Handler前,主动调用下Looper.prepare()
每个线程的的Looper#prepare相对所在线程只能被调用一次,否则会报"Only one Looper may be created per thread"(参见Looper#prepare),之所以主线程直接创建Handler不会抛出类似异常,是因为在程序启动时,系统已经帮我们调用了Looper#prepare(参见ActivityThread#main)
7.消息池子
Message中有一个单链表的sPool用来缓存消息,最大缓存数量是50,所以当我们要使用Handler发送消息时,可以通过Handler#obtainMessage()或者Message.obtain()来得到一个消息对象,而不是通过new Message()来得到消息。
8.epoll 阻塞/唤醒机制
Android从2.3开始,将Object#wait()/notify()的阻塞唤醒机制改为Linux的epoll机制。
java层的looper、messageQueue在c++层均有对应的类,然后通过将looper初始化时创建的 eventFd 返回的 wakeEventFd,注册到 epoll_create创建的 epoll对象里,然后通过epoll_ctl去在wakeEventFd上添加一个 ADD类型的事件监听,最后上层 调用nativePollOnce的时候,最终会调用 epoll 的 epoll_wait 通过监听fd的方式来形成阻塞。来消息后,去往wakeEventFd去写一个“1”值,此后epoll_wait监听到值了,就阻塞解除继续执行了,继续取message执行去了。具体可参考这篇文章
关于ThreadLocal可以看看这篇文章 https://www.jianshu.com/p/517f3d16ad89