一张图展现Handler消息机制

2018-02-27 11:17:57来源:https://www.jianshu.com/p/9a50553da6cc作者:Jdqm人点击

分享


文字总感觉很难描述,还不如来一张图总结一下来得清晰。





Handler
1.Looper.prepare()

创建Looper,并保存到sThreadLocal中,创建了一个MessageQueue并赋值给Looper#mQueue


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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

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()来得到消息。


关于ThreadLocal可以看看这篇文章 https://www.jianshu.com/p/517f3d16ad89








最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台