如果对handler了解不多先看上一篇handler基础:
Handler又是什么鬼东西
1. Hadler的作用
handler是用于线程间通信的,但是它并不是仅仅用于处理UI界面,而handler更多的是整个app通信的框架。Android是基于事件驱动的,即所有的activity生命周期都是通过handler事件驱动的。
既然handler这么重要,那它的线程安全就至关重要,关系着整个app的运行,那么它是如何保证自己的线程安全的呢?
2. MessageQueue消息管理
Handler机制里最主要的类MessageQueue,这个类就算所有消息的存储仓库,在这个仓库中,同时插入和取出消息,我们如何管理好消息,就是handler线程安全的关键点了
消息管理主要包括两点:
- 消息入库(enqueueMessage)
- 消息出库(next)
所以这两个接口是确保线程安全的主要档口。
3. enqueueMessage源码
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;// when执行的时间点
Message p = mMessages;// mMessages队列头部的msg
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;//注意此处,如果当前的队列是出于阻塞的状态,那么mBlocked就会为true
} 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);// 调用native方法,唤醒阻塞;
}
}
}
//锁结束的地方
synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机的。this也就是MessageQueue
synchronized(this)
这个锁说明的是,对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的;
在我们Handler中,一个线程对应一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue,所以我们的主线程中就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其他的都需要等待,这样就不会出现消息队列混乱的问题
4. next()源码
Message next() {
....
for (;;) {
....
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
...
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}//synchronized 结束之处
// 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;
}
}
看源码发现,从线程中取消息,每次都是队列的头部取,那么它加锁是不是没有意义呢?答案当时是否定的。
我们必须在next里加锁,因为由于synchronized(this)
作用范围是所有this正在访问的代码块都会有保护作用,也就是它可以保证next函数和enqueueMessage函数能够实现互斥,这样才能真正的保证多线程访问的时候,messageQueue可以有序的进行