Android 的消息机制主要是指 Handler 的运行机制以及 Handler 所附带的 MessageQueue 和 Looper 的工作过程。
从开发的角度来看,Handler 是 Android 消息机制的上层接口,通过它我们可以轻松的将一个任务切换到 Handler 所在的线程中去执行。
MessageQueue 的中文翻译是消息队列,内部采用单链表的数据结构来存储消息列表。
Looper 的中文翻译是循环,可以理解为消息循环。由于 MessageQueue 只是一个消息的存储单元,它不能去处理消息,而 Looper 就填补了这个功能,Looper 会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。
Looper 中还有一个特殊的概念:ThreadLocal,当我们调用 Looper.prepare() 方法创建 Looper 时,就会使用到它。下面就先来简单看看 ThreadLocal。
ThreadLocal 的工作原理
ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据。
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用 ThreadLocal。比如对于 Handler 来说,它需要获取当前线程的 Looper,显然 Looper 的作用域就是线程并且不同线程具有不同的 Looper,这个时候通过 ThreadLocal 就可以轻松实现 Looper 在线程中的存取。
ThreadLocal 另一个使用场景是复杂逻辑下的对象传递,比如监听器的传递,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,有需要监听器能够贯穿整个线程的执行过程,这个时候就可以采用 ThreadLocal 让监听器作为线程内的全局对象存在,在线程内部只要通过 get 方法就可以获取到监听器。不采用 ThreadLocal 的话,通常可能采用两种方法:一是将监听器通过参数形式在函数调用栈中进行传递,二是将监听器作为静态变量供线程访问。第一种方法问题在于当函数调用栈很深的时候,通过函数参数来传递监听器对象几乎是不可接受的。第二种方法问题在于不具有可扩充性,有多个线程在执行的话就需要提供多个静态地监听器对象。
ThreadLocal 实例演示:
定义一个 Boolean 类型 ThreadLocal 对象:
private ThreadLocal<Boolean> mBooleanThreadLocal = new ThreadLocal();
分别在主线程、子线程 1 和子线程 2 中设置和访问它的值:
mBooleanThreadLocal.set(true);
print(mBooleanThreadLocal.get());
new Thread("Thread1") {
@Override
public void run() {
mBooleanThreadLocal.set(false);
print(mBooleanThreadLocal.get());
};
}.start();
new Thread("Thread2") {
@Override
public void run() {
print(mBooleanThreadLocal.get());
};
}.start();
这个时候,主线程中打印结果为 true,子线程1中打印结果为 false,子线程2中打印结果为 null。
虽然在不同线程中访问的是同一个 ThreadLocal 对象,但是它们通过 ThreadLocal 获取到的值确实不一样的。是因为不同线程访问同一个 ThreadLocal 的 get 方法, ThreadLocal 内部会从各自的线程中取出一个数据集合,然后再从数据集合中根据当前 ThreadLocal 的索引去查找出对应的值。因为不同线程中的数数据集合不同的,所以通过 ThreadLocal 可以在不同的线程中维护一套数据的副本并且彼此互不干扰。
源码分析:
ThreadLocal 是一个泛型类,它的定义为 public class ThreadLocal,其中 set 方法如下:
public void set(T value) {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 通过当前线程去获取到 ThreadLocalMap
if (map != null)
map.set(this, value); // 如果不为空,直接调用 set 方法
else
createMap(t, value); // 如果为空,调用 createMap 创建
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; // 直接返回线程实例的成员变量 ThreadLocal.ThreadLocalMap
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); // 直接初始化并赋值给线程实例的 threadLocals
}
ThreadLocalMap 是自定义的哈希映射表。 没有操作暴露到 ThreadLocal 类之外。 该类是包私有的,以允许在 Thread 类中声明字段。 为了保证较长的使用寿命,哈希表条目使用 WeakReferences 作为键。 但是,由于不使用参考队列,因此当表在内存空间不足时,才保证删除过时的条目。即 ThreadLocalMap 就是线程各自持有的数据集合,通过 ThreadLocal 实例作为 key 来存取 value。
get 方法如下:
public T get() {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 通过当前线程去获取到 ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result; // 如果不为空,就返回当前 ThreadLocal 对应的 value
}
}
return setInitialValue(); // 如果获取对应 value 失败则设置并返回初始值。
}
private T setInitialValue() {
T value = initialValue(); // 设置初始值
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 通过当前线程去获取到 ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
从 ThreadLocal 的 set 和 get 方法可以看出,它们所操作的对象都是当前线程的 ThreadLocalMap,因此在不同线程中访问同一个 ThreadLocal 的 set 和 get 方法,它们对 ThreadLocal 所做的读写操作仅限于各自线程的内部,所以 ThreadLocal 可以在多个线程中互不干扰地存储和修改数据。
Android 的消息机制
MessageQueue 的主要实现
MessageQueue 就是 Android 中的消息队列,消息机制中比较难理解的部分就在这里,所以先记录一下这部分的内容。消息队列主要包含两个重要操作:插入和获取。获取操作本身会伴随着删除操作,插入和获取对应的方法分别为 enqueueMessage 和 next,其中 enqueueMessage 的作用是往消息队列中插入一条消息,而 next 的作用是从消息队列中取出一条消息并将其从消息队列中移除。
enqueueMessage 的源码如下:
boolean enqueueMessage(Message msg, long when) {
// msg.target 为空,即 Handler 为空,即调用此方法前,Handler 会调用 msg.target = this
// 所以 msg.target 通常不为空
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;// 这里将全局变量 mMessages 赋值给变量 p
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 以下三种情况进入此逻辑
// 1、p 为空,即全局变量 mMessages 的值为 null
// 2、when == 0,when 为准确的时间值,比如来源于 SystemClock.uptimeMillis()
// 3、when < p.when,即当前 msg 的时间小于全局变量 mMessages 的时间
// 综上可以认为,当 msg 为需要第一时间处理的消息时便会进入此逻辑
msg.next = p;// 然后将 msg 插入消息队列队首
mMessages = msg;// 全局变量 mMessages 设为 msg,所以 mMessages 为消息队列队首的消息
needWake = mBlocked;// 将 mBlocked 赋值给 needWake,从源码可知 mBlocked 只在 enqueueMessage 和 next 方法中使用
} else {
// 所以这里处理的是非第一时间处理的消息,即不需要将 msg 插入消息队列队首的情况
// 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; // 将 prev 赋值为 p,初始 p 为 mMessages,也即队首消息
p = p.next;// p 赋值为下一条消息
if (p == null || when < p.when) {
// 1、p == null,即没有下一条消息了
// 2、when < p.when,即当前的 msg 时间比下一条消息的时间更早
// 这里可以看到是在遍历消息队列根据 msg 来寻找消息插入位置
// 所以消息队列是根据时间 when 值从小到大排列的
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 根据前面逻辑找到的需要插入的位置的前一条消息 prev 和后一条消息 p,将 msg 插入消息队列
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
// native 方法,唤醒消息队列
nativeWake(mPtr);
}
}
return true;
}
从实现上看,它的主要操作其实就是单链表的插入操作。
关于 next 方法:
@UnsupportedAppUsage
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.
// mPtr 是一个和 native 代码相关的全局变量
// 它在 MessageQueue 的构造方法中调用 native 方法 nativeInit() 被赋值
// 在 MessageQueue 的 dispose() 方法中调用 native 方法 nativeDestroy(mPtr) 后,被重新赋值为 0
// 可以知道当消息循环已经退出并被处理时,mPtr 值为 0
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 类的 native 方法
Binder.flushPendingCommands();
}
// native 方法
// 该方法用于“等待”,等待事件发生或者超时
// nextPollTimeoutMillis 为 0 时立即返回,为 -1 时无限等待
// 无限等待时必须主动唤醒,enqueueMessage 方法中的 nativeWake 方法即可唤醒
// 利用名为 epoll 的 Linux 系统调用, 该系统调用可以监视文件描述符中的 IO 事件
// native 层的消息机制相关
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;// 由上文可知,mMessages 全局变量为全局变量为消息队列队首的消息
if (msg != null && msg.target == null) {
// msg.target 为 Handler 将 Message 插入 MessageQueue 队列时的 enqueueMessage 方法中
// 通过 msg.target = this 赋值的,即 msg.target 为 Handler 实例。
//
// 我们知道,同步消息通过 MessageQueue 的 enqueueMessage 加入队列
// 并且 msg.target == null 时将会抛出异常。
// 所以什么时候这里的 msg.target == null 呢?
// postSyncBarrier(long when) 方法中,会将同步障碍器(synchronization barrier)加入消息队列
// 这是一个特殊的 Message 对象,target 为空,argq1 为 token
//
// 所以当前 msg 为同步障碍器消息时,进入代码块执行以下逻辑
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 在队列中查找下一条异步消息,找到后 msg 即为异步消息
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.
// 当前时间小于 msg 的时间,说明 msg 还没有就绪,设置超时以使其在准备就绪时醒来。
// 超时时间设为 msg 的时间和当前时间的差值
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
// 此时 msg 已经就绪
mBlocked = false;
if (prevMsg != null) {
// prevMsg 不为空,说明跳过了一些消息队列中的 Message
// 所以将 msg 移出时要把 msg 之前和之后的队列部分连接起来
prevMsg.next = msg.next;
} else {
// prevMsg 为空,说明此前 msg 为队首消息,
// 所以将 msg 移出时要把代表队首消息的 mMessages 全局变量设为 msg.next
mMessages = msg.next;
}
// 最终将当前 msg 返回
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
// 没有找到 Message 时设置超时时间 nextPollTimeoutMillis = -1
// 此时执行 nativePollOnce 方法会进入永久阻塞
nextPollTimeoutMillis = -1;
}
// 综上,当存在就绪的消息时,返回该 msg;否则设置超时时间 nextPollTimeoutMillis,然后继续后面的逻辑
// Process the quit message now that all pending messages have been handled.
// 就绪的消息都处理完了,当前没有就绪的消息时,如果设置了退出,即调用了 quit(boolean safe) 方法时
// 会调用 dispose() 方法处理退出,并返回 null
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 初始为 -1
// 当 mMessages 为空(即消息队列中没有消息)或者当前时间小于 mMessages 的时间(消息队列中没有已就绪的消息)
// 会将 pendingIdleHandlerCount 设为 mIdleHandlers 的 size,即 IdleHandler 集合中存储的数量
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
// 此时 pendingIdleHandlerCount <= 0 表示没有 IdleHandler
// 没有就绪的消息,那么 mBlocked 设为 true,表示消息队列进入阻塞状态
// 调用 continue 继续循环
mBlocked = true;
continue;
}
// 如果存在 IdleHandler,那就构造 IdleHandler[],将 mIdleHandlers 转为 IdleHandler[] 类型的全局变量 mPendingIdleHandlers
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
// 下面便是调用 IdleHandler 的 queueIdle 方法
// 也就是 IdleHandler 闲时执行机制的接口方法执行的地方
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// IdleHandler 的 queueIdle() 方法如果返回 false,则会在 IdleHandler 集合中移除此 idler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 综上,消息队列空闲时会遍历执行 IdelHandler 集合中每个对象的的 queueIdle() 方法
// 这里实现了 IdleHandler 的闲时执行机制
// queueIdle 方法返回 false 则执行完毕之后移除这条消息
// 如果返回 true 则保留,等到下次空闲时会再次执行
// 重置表示持有 IdleHandler 的数量的变量为 0
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// 重置超时时间为 0
// 因为在处理 IdleHander 时,可能会加入新消息,所以重置超时时间为 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 方法是一个无限循环的方法,如果消息队列中没有消息,那么 next 方法会一直阻塞在这里。当有新消息到来时,next 方法会返回这条消息并将其从单链表中移除。
关于 next 方法阻塞的机制。这里涉及到 Linux pipe/epoll 机制,当消息队列中没有消息时,那么 next 方法会一直阻塞在 nativePollOnce() 方法里,此时主线程会释放 CPU 资源进入休眠状态,直到有新消息到来时,通过往 pipe 管道的写端写入数据来唤醒主线程工作。这里采用的 epoll 机制,是一种 IO 多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步 I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量 CPU 资源。
handler 延时发送消息是怎么实现的呢?首先,从 enqueueMessage 方法中可以知道,发送延迟消息时是将消息插入到消息队列中,这里开启了一个循环,遍历了消息队列里面的消息,将消息的延时参数 when 较小的消息放在了消息队列前段。因此消息队列中的消息是按照延时参数 when 的大小排列的。其次,next 方法中取出消息后,会比较当前时间 now 与消息的时间参数 when 的大小,当未达到消息的时间时,会计算出当前时间和消息的时间参数的差值 nextPollTimeoutMillis,这个值会作为下一次循环时 nativePollOnce() 方法中的超时参数,表明阻塞的超时时间。当在这个时间内还没有新的消息到来,会唤醒线程,然后返回这消息,从而达到了延时发送消息的目的。
IdleHandler 闲时执行机制是怎么实现的呢?next() 方法中,从消息队列中没有找到已经就绪的消息时,即此刻消息队列空闲,next() 方法中会遍历执行 IdelHandler 集合中每个对象的的 queueIdle() 方法。queueIdle 方法返回 false 则执行完毕之后移除这条消息;如果返回 true 则保留,等到下次空闲时会再次执行。
关于阻塞和唤醒,从上面的源码分析可以得知:
1、队列中没有任何消息时,next 方法中的 nextPollTimeoutMillis 参数会被赋值为 -1,后续 mBlocked 被赋值为 true,那么下一次 for 循环的 nativePollOnce 方法会进入永久阻塞。直到 enqueueMessage 方法中加入新的 Message,needWake 才会被赋值为 mBlocked,即 true,后续才会调用 nativeWake 方法。
2、当队首消息为未满足时间条件的消息,next 方法中 nextPollTimeoutMillis 参数会被赋值为时间差值,后续 mBlocked 也被赋值为 true,那么下一次 for 循环的 nativePollOnce 方法会进入临时阻塞,直到经过 nextPollTimeoutMillis 时间后定时唤醒。期间如果 enqueueMessage 方法中加入新的 Message 到队首,needWake 会被赋值为 true,继续调用 nativeWake 方法主动唤醒。
3、如果队首消息为同步障碍器消息(SyncBarrier)。
1)当队列中不含异步消息,next 方法中的 nextPollTimeoutMillis 参数会被赋值为 -1,后续 mBlocked 被赋值为 true,那么下一次 for 循环的 nativePollOnce 方法会进入永久阻塞。只有新加入同步消息到队首,或者加入异步消息,needWake 才会被赋值为 true,然后调用 nativeWake 方法主动唤醒。
2)当队列中含有未满足时间条件的异步消息,next 方法中 nextPollTimeoutMillis 参数会被赋值为时间差值,后续 mBlocked 也被赋值为 true,那么下一次 for 循环的 nativePollOnce 方法会进入临时阻塞,直到经过 nextPollTimeoutMillis 时间后定时唤醒。唤醒后因为队首消息依然为之前的同步障碍器消息,所以仍旧优先处理异步消息。a、临时阻塞期间,由于队首为同步障碍器,当加入消息到队首时, needWake 为 true,调用 nativeWake 方法主动唤醒,处理完若干新消息后,当前队首又回到之前的同步障碍器。b、当加入新的同步消息到非队首时,needWake 为 false。c、当加入新的异步消息到非队首时,needWake 为 true,调用 nativeWake 方法主动唤醒,由于队首为同步障碍器,如果该异步消息未满足时间,则重复 2)逻辑;否则优先返回该异步消息进行处理。
关于同步障碍器,由上面的分析可以得知:
当队首为同步障碍器消息时,同步障碍器消息的时间顺序后面的同步消息都不会进行处理。那么同步消息不会一直得不到处理吗?参见下方 removeSyncBarrier 方法。通过调用 removeSyncBarrier 方法来移除同步障碍器消息。
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
// 这里通过 while 循环遍历队列,找到指定的同步障碍器消息
// 这里判断了 arg1 和入参 token 相等
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
// 没有找到指定的同步障碍器消息,抛出异常
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
// 同步障碍器在消息队列中间位置,移除同步障碍器消息时需要做队列拼接
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;// 重新赋值队首消息
needWake = mMessages == null || mMessages.target != null;// 没有队首消息或队首消息不会同步障碍器时 needWake 为 true
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
// 唤醒消息队列的阻塞方法
nativeWake(mPtr);
}
}
}
同步障碍器消息的使用。参考 ViewRootImpl.java 中的 scheduleTraversals 方法和 unscheduleTraversals 方法。即在 View 的绘制流程中,会向主线程的 Handler 添加同步障碍器消息。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 添加同步障碍器消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步障碍器消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
通常的开发过程中一般不会使用异步消息和同步障碍器消息,因为他们相关的方法对应用开发来说都是 hide 的。(狗头)
Lopper 的工作原理
Looper 在 Android 的消息机制中扮演着消息循环的角色,具体来说就是它会不停地从 MessageQueue 中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
Looper 的构造方法:
private Looper(boolean quitAllowed) {
// 创建 MessageQueue 消息队列 mQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
创建 MessageQueue 消息队列 mQueue,并将当前线程保存为全局变量 mThread。
Looper 的构造函数是一个私有的方法,下面是 Looper 中调用构造方法的地方,即 Looper.prepare() 方法中:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {// 判断当前线程有无初始化 Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));// sThreadLocal 存储的是 Looper 类型的数据。
}
可以看到,Looper 的构造方法中出现了 ThreadLocal 实例 sThreadLocal,并存储了新构造的 Looper 实例,由此来实现 Looper 实例在线程中的唯一性。
同时还提供了获取主线程的 Looper 相关的方法:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
prepareMainLooper 方法主要是给主线程也就是 ActivityThread 创建 Looper 使用的,其本质也是通过 prepare 方法来实现。由于主线程的 Looper 比较特殊,所以 Looper 提供了一个 getMainLooper 方法,通过它可以在任何地方获取到主线程的 Looper。
退出一个 Looper:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
quit 会直接退出 Looper,而 quitSafely 只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全退出。调用的是 MessageQueue 的 quit(boolean safe) 方法。
Looper 退出后,通过 Handler 发送的消息会失败,这个时候 Handler 的 send 方法会返回 false。在子线程中,如果手动为其创建了 Looper,那么在所有的事情完成以后应该调用 quit 方法来终止消息循环,否则这个子线程就会一直处于等待的状态,而如果退出 Looper 以后,这个线程就会立刻终止,因此建议不需要的时候终止 Looper。
通过 Looper.loop() 开启消息循环:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 获取当前线程的 Looper
final Looper me = myLooper();
// 如果当前线程的 Looper 为空,则会抛出异常
// 主线程会自行调用 Looper.prepare() 方法,子线程需要手动调用。
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 拿到和当前 Looper 绑定的 MessageQueue 对象 queue
final MessageQueue queue = me.mQueue;
// Binder 的 native 方法,重置当前线程上的传入 IPC 的身份。
// 返回一个 token,这个 token 可以通过 restoreCallingIdentity(long) 方法还原线程上的传入的身份信息。
// 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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
// 调用 MessageQueue 的 next() 方法,该方法可能会阻塞
// 具体阻塞原因在上文 MessageQueue 的 next 方法分析中可以看到
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 调用 msg 持有 Handler 对象的 dispatchMessage 方法
// 将 msg 交给 handler 对象处理这条消息
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);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 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();
}
}
loop 方法中有一个死循环,唯一跳出循环的方式是 MessageQueue 的 next 方法返回了 null。当 MessageQueue 的 next 方法就会返回 null 时,Looper 会直接返回,否则 loop 方法就会无限循环下去。MessageQueue 的 next 方法是一个阻塞操作,当没有消息时 next 方法会一直阻塞,loop 方法也就会一直阻塞。如果 MessageQueue 的 next 方法返回了新消息,loop 就会处理这条消息。msg.target.dispatchMessage(msg) 中的 msg.target 是发送这条消息的 Handler 对象,这样 Handler 发送的消息最终又交给它的 dispatchMessage 方法来处理。Handler 的 dispatchMessage 方法是在创建 Handler 时所使用的 Looper 中执行的,而我们可以在其他子线程中 sendMessage,这样就成功地将代码逻辑切换到指定的线程中去执行了。
Handler 的工作原理
Handler 的工作主要包含消息的发送和接收过程。消息的发送可以通过 post 的一系列方法以及 send 的一系列方法来实现,post 的一系列方法是通过 send 的一系列方法来实现的。
先看看 Handler 的构造方法:
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback) {
this(callback, false);
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
@UnsupportedAppUsage
public Handler(boolean async) {
this(null, async);
}
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
通过上面的构造方法可以知道,如果当前线程没有 Looper 的话,就会抛出异常,这也解释了在没有 Looper 的子线程中创建 Handler 会引发程序异常的原因。
发送消息的典型过程:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// 拿到当前 Handler 绑定的 MessageQueue 对象 queue。
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 这里将 msg 的 target 变量赋值为 this,即当前 Handler 对象
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
// 设置是否为异步消息
// mAsynchronous 在构造方法中被赋值
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 调用 MessageQueue 的 enqueueMessage 方法并返回方法结果
// 也就是向 MessageQueue 消息队列中插入了一条消息
return queue.enqueueMessage(msg, uptimeMillis);
}
最后可以看到,Handler 发送消息的过程仅仅是向 MessageQueue 消息队列中插入了一条消息。MessageQueue 通过 next 方法取出消息交给 Looper,Looper 又通过 msg.target 交还给 Handler 处理,即 Handler 的 dispatchMessage 方法会被调用,进入消息处理阶段。
Handler 的 dispatchMessage 方法:
public void dispatchMessage(@NonNull Message msg) {
// 首先检查 Message 的 callback 是否为空
if (msg.callback != null) {
handleCallback(msg);
} else {
// 检查 mCallback 是否为空
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
1、首先,检查 Message 的 callback 是否为空,不为空就通过 handleCallback 方法来处理消息。
private static void handleCallback(Message message) {
message.callback.run();
}
Message 的 callback 是一个 Runnable 对象,实际上就是 Handler 的 post 方法所传递的 Runnable 参数。
2、其次,检查 mCallback 是否为空,不为空就调用 mCallback 的 handleMessage 方法来处理消息。
mCallback 是一个接口的实例,它的定义如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
如下源码所示,可以通过传入 Callback 参数的方式来创建 Handler 对象:Handler handler = new Handler(callback)。
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(@Nullable Callback callback) {
this(callback, false);
}
Callback 可以用来创建一个 Handler 的实例但并不需要派生 Handler 的子类。日常开发中创建 Handler 最常见的方式就是派生一个 Handler 的子类并重写其 handleMessage 方法来处理具体的消息,而 Callback 提供了另外一种使用 Handler 的方式,当不想派生子类时,就可以通过 Callback 来实现。
3、最后,调用 Handler 的 handleMessage 方法来处理消息。
理解消息机制实现线程间通信
static class MyHandler extends Handler {
WeakReference<Activity> mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what){
...
}
}
}
private MyHandler handler = new MyHandler(activity);
private void do(){
new Thread(new Runnable() {
@Override
public void run() {
...
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg);
...
}).start();
}
上面是我们使用消息机制完成子线程发送消息到主线程的实例,整个发送消息的过程具体可以这么理解:
首先,我们在主线程创建了 MyHandler 实例,MyHandler 继承自 Handler。而 Handler 的构造方法中获取了当前线程的 Looper,由于当前是主线程,所以主线程的 Looper 已经创建好了,无需调用 Looper.prepare() 方法。自然 Looper 中也存在主线程的消息队列 MessageQueue。
然后,当我们执行 do 方法,开启子线程并在线程中通过 Message.obtain() 构造 Message 实例 msg,然后通过全局变量 hander,调用 sendMessage 方法发送 msg,特别说明此时 msg.target 在 sendMessage 里面的 enqueueMessage 方法中被设置为当前 handler,最后 msg 消息即被加入主线程的消息队列中。
接下来,当主线程的 Looper 取出 msg 消息后,通过 msg.target 的 dispatchMessage 方法分发消息,即最后回到了 handler 的 handleMessage 方法中去。
由此,完成了线程之间的通信。