关于handler消息处理机制,只要一提到,相信作为一个android工程师,脑海就会有这么一个流程
大家都滚瓜烂熟了,但别人问到几个问题,很多人还是栽到这个“烂”上面,比如:
- 一个线程是如何对应一个Lopper的?
- messageQueue是如何做到线程安全的?
首先先Looper看一段代码:
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));
}
这里先通过sThreadLocal.get()去获取这个Looper, 获取不到再去通过new (Looper(quitAllowed))去创建Looper()对象。再来看看这个构造函数:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
构造函数私有的,只能通过Prepare去初始化,这里就形成了一个关系,一个Looper 对应一个MessageQueue。
回过头再来分析,线程是如何和Looper对应的。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
在Prepare()的时候给sThreadLocal.set(Looper), 获取Looper的对象是通过sThreadLocal.get()返回的,下面看看sThreadLocal:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这是一个静态成员变量所有的Looper对象共有属性。
sTreadLocal保存线程的唯一值,这样就保证一个线程对应一个looper.
至于MessageQueue的线程同步,会有奇葩的人问到,无非就是syncnized关键字,或者加锁。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这里用的是一个加锁的方式。
在Loop.loop中有一死循环,那么当没有消息时,主线程不可能死掉吧?
“`
在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
还有什么关于handler的问题,希望大家可以提出来。