Handler通信原理
Handler,Message,Looper,MessageQueue之间得关系
总结:
Looper有一个MessageQueue消息队列;
MessageQueue有一组待处理的Message;
Message中有一个用于处理消息的Handler;
Handler中有Looper和MessageQueue
在日常工作中有很多地方都会使用handle,比如更新ui界面或者线程间通信,正常主线程使用就不说了,注意下不要内存泄漏就可以了,关于这个可以参考内存泄漏和内存溢出以及优化,这里主要看线城中使用,我们都知道在线城中我们都需要先Looper.prepare()
然后再Looper.loop()
,否则就会报No Looper; Looper.prepare() wasn't called on this thread
具体原因就要在源码中查找了,首先看下Looper.prepare()
部分
frameworks/base/core/java/android/os/Looper.java
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));
/// M: ALPS00297986
if (!IS_USER_BUILD) {
long instances = VMDebug.countInstancesOfClass(Looper.class, false);
// check if the looper instance over a limit, it should has some leakage.
if (100 < instances) {
Log.e(TAG, "WARNING: The Looper class instance count has over a limit(100). There should be some leakage of Looper or HandlerThread.");
Log.e(TAG, "Looper class instance count = " + instances);
Log.e(TAG, "Current Thread Name: " + Thread.currentThread().getName());
Thread.currentThread().getThreadGroup().list();
Thread.currentThread().dumpStack();
} //if
}
/// M: ALPS00297986
}
可以看到进入private static void prepare(boolean quitAllowed)
方法后会先判断sThreadLocal.get()
的值,sThreadLocal
的定义如下
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这边会返回一个Looper对象,如果当前线程中已经存在looper循环则会抛出Only one Looper may be created per thread
,一个线程中只能存在一个looper循环,否则就会进入sThreadLocal.set(new Looper(quitAllowed));
,这里面会创建looper并绑定sThreadLocal中
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到这里面是创建对应的MessageQueue并绑定当前线程
再看下Looper.loop()
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
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
/// M: MSG Logger Manager @{
if (!IS_USER_BUILD) {
Printer msglogging = me.mMsgMonitorLogging;
if (msglogging != null) {
msglogging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
if (MessageMonitorLogger.monitorMsg.containsKey(msg)) {
MessageMonitorLogger.MonitorMSGInfo monitorMsg = MessageMonitorLogger.monitorMsg.get(msg);
if (MessageMonitorLogger.mMsgLoggerHandler.hasMessages(MessageMonitorLogger.START_MONITOR_PENDING_TIMEOUT_MSG, monitorMsg)) {
Log.d("Looper", "RemoveMessages PENDING_TIMEOUT_MSG msg= " + msg);
MessageMonitorLogger.mMsgLoggerHandler.removeMessages(MessageMonitorLogger.START_MONITOR_PENDING_TIMEOUT_MSG, monitorMsg);
try {
if (monitorMsg.executionTimeout > 100) {
Message msg1 = MessageMonitorLogger.mMsgLoggerHandler.obtainMessage(MessageMonitorLogger.START_MONITOR_EXECUTION_TIMEOUT_MSG, monitorMsg);
MessageMonitorLogger.mMsgLoggerHandler.sendMessageDelayed(msg1, monitorMsg.executionTimeout);
} else {
MessageMonitorLogger.monitorMsg.remove(msg);
if (monitorMsg.executionTimeout != MessageMonitorLogger.DISABLE_MONITOR_EXECUTION_TIMEOUT_MSG)
throw new IllegalArgumentException("Execution timeout <100 ms!");
}
} catch (IllegalArgumentException e) {
Log.d(TAG, "Execution timeout exception " + e);
}
}
}
}
/// M: MSG Logger Manager @}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
/// M: MSG Logger Manager @{
if (!IS_USER_BUILD) {
Printer msglogging = me.mMsgMonitorLogging;
if (msglogging != null) {
msglogging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
if (MessageMonitorLogger.monitorMsg.containsKey(msg)) {
MessageMonitorLogger.MonitorMSGInfo monitorMsg = MessageMonitorLogger.monitorMsg.get(msg);
if (MessageMonitorLogger.mMsgLoggerHandler.hasMessages(MessageMonitorLogger.START_MONITOR_EXECUTION_TIMEOUT_MSG, monitorMsg)) {
Log.d("Looper", "RemoveMessages EXECUTION_TIMEOUT msg=" + msg);
MessageMonitorLogger.mMsgLoggerHandler.removeMessages(MessageMonitorLogger.START_MONITOR_EXECUTION_TIMEOUT_MSG, monitorMsg);
MessageMonitorLogger.monitorMsg.remove(msg);
}
}
}
/// MSG Logger Manager @}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
这段代码很长,主要就是一个死循环来读取MessageQueue中的消息,在开始会通过myLooper()
函数来检查当前线程中是否存在looper,如果没有则会抛出之前看到的No Looper; Looper.prepare() wasn't called on this thread.
异常
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
public static Looper myLooper() {
return sThreadLocal.get();
}
这里比较有意思的是Message msg = queue.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 (false) Log.v("MessageQueue", "Returning message: " + msg);
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("MessageQueue", "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;
}
}
代码中的nativePollOnce(ptr, nextPollTimeoutMillis)方法是阻塞的原因,但是却不会导致anr,原因是组件的ANR是由不同的类进行判断是否要发出ANR异常,让系统去处理而epoll阻塞并不在这些类的控制范围内,关于这部分可参考腾讯Android面试:Handler中有Loop死循环,为什么没有阻塞主线程,原理是什么
再看下loop()
的msg.target.dispatchMessage(msg)
方法,msg.target的对象是handler,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在这里面可以看到消息的分发也是有优先级的msg.callback–>mCallback–>handleMessage
msg.callback是Message的回调,mCallback和handleMessage都是Handler自身的
在loop()方法最后会调用msg.recycleUnchecked()对msg对象进行回收
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
hasRecycle = true;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
总结:
Looper.prepare()
负责检查sThreadLocal中是否存在looper,如果不存在则调用looper的构成方法,创建MessageQueue并绑定当前线程
Looper.loop()
主要负责消息的分发处理
final Looper me = myLooper(); 获取当前线程的looper对象,如果不存在则会抛异常
for (;;)死循环中的主要流程
Message msg = queue.next() 链表中消息的逐条读取,没有消息时则会阻塞等待新的消息到来
msg.target.dispatchMessage(msg) 调用handler的方法进行消息的分发
msg.recycleUnchecked()对象进行回收
Message
Message是一个消息类,我们平时传递消息是都会使用他,虽然在他的构造方法是public的但是我们最好使用obtain的方式去获取它的实例
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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--;
/// M: Add message protect mechanism
m.hasRecycle = false;
return m;
}
}
return new Message();
}
在我们用obtain获取实例时他并不会直接new一个Message给我们,而是会查看是否存在sPool这样的缓存,如果存在则会复用
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
hasRecycle = true;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
在回收msg时是会有sPool这样的缓存存在的
关于handle引用的内存泄漏请查看内存泄漏和内存溢出以及优化
如有问题欢迎指出,共同进步才是真的进步