Handler机制原理初步分析:
handler机制涉及:handler,Looper,Messager和MessagerQueue
Looper属于线程私有的,通过LocalThreadMap来达到Looper和线程一对一的关系。Looper会不停的去MessagerQueue里面取Messager,然后执行handler的handlerMessage方法。
handler和Looper属于n对1的关系,handler在创建的时候会根据当前线程和对应的Looper做绑定。然后handler就可以发送Messager到对应Looper的MessageQueue里面,Messager则是一个对消息的封装类,它持有handler的句柄,所以handler容易内存泄漏也是因为如此,在消息很多的情况下,Messager创建如果通过new的方式创建,会造成内存抖动,所以通过obain方式创建。
MessagerQueue:是一个队里,先进先出,内部结构是链表结构,方便插入和删除。
源码深入分析:
1.关于Looper的创建:我们主要分析两个方法prepareMainLooper()和prepare()
prepare(), 如果ThreadLocal里面没有对应的Looper,就重新new,并更新到ThreadLocal里面,ThreadLocal是线程私有内存,堆里面开辟了一块内存区域,这个区域属于线程私有。
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构造函数里面对MessageQueue也相应的做了初始化。
Deprecated The main looper for your application is created by the Android environment, so you should never need to call this function yourself.
prepareMainLooper()方法有这么一段注释,说明主线程的Loop一开始就存在了。不需要手动执行。
简单做个总结:
1.子线程如果要使用handler机制,那么需要手动调用prepare(),主线程不用。
2.Looper对象存在线程私有内存,和线程一对一绑定。
2.看下Looper的loop方法:
public static void loop() { /..省略部分代码../ for (;;) { if (!loopOnce(me, ident, thresholdOverride)) { return; } } } private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return false; } /..省略部分代码../ try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } /..省略部分代码../ return true; }
我们看到message为null的时候,loop方法停止,当拿到msg,就执行msg的target的dispatchMessage的方法。如果设置了Observer,则会执行observer回到,我们可以设置observer去捕获一些异常。
3.再看下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); 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) { // 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 { // 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 { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
这里加了同步关键字,主要防止插入msg和取msg得时候出现并发错误。
拿到msg之后判断了target是否是null,这里需要注意下只有同步屏障的消息target才是null,这里我们看到,如果是屏障消息就循环遍历去拿异步消息,优先执行异步消息,屏蔽普通消息。
之后会去判断when,执行时间是否正常,如果要等就计算出等待时间,然后触发线程等待。
最后在空闲时间执行IDEHandler。
同步屏障:
同步屏障用来保证异步消息第一时间被执行,比如view的绘制的时候,messageQueue里面有很多任务等待执行,如果不及时处理绘制任务,会导致界面卡顿。
1.同步屏障必须要及时取消,否则普通消息不能执行。
2.如果当前页面不停的执行重绘,会导致IDEHandler无法执行。
IDEHandler:
用来执行低优先级的任务,比如我们可以把一些内存的数据在空闲时更新到缓存,下次取得时候更快一点,当然如果没更新成功,也不影响使用,只不过会影响一点点体验。
4.看下messageQueue的插入消息部分:
代码不贴了,主要是遍历链表,按照when的从小到大顺序插入合适的位置。然后执行nativeWake来唤醒。
5.handler相关:
public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
上面我们看到,优先执行msg的callback,然后执行handler里面的callback,如果返回true则不执行handlermessage,false情况下会再处理handlerMessage方法。
6.message:
message主要看下如何解决内存抖动的,这段代码我们看到,Message内部管理了一个sPool,最大数量为50,不通过new的方式,通过从池子里面拿message对象,然后被拿走之后会置为null,并计数器减1。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
然后去Looper的Loop方法里面看到
msg.recycleUnchecked();
会将已经处理的msg放到池子里。