「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。
Looper的工作原理
prepare方法
之前说过ThreadLocal和MessageQueue,这次再说一下Looper,Looper是属于一个线程的,通常通过Looper的类方法Looper.prepare()来创建,接着通过Looper.loop()方法开启消息循环,作用是不断从消息队列MessageQueue中取出Message,如果MessageQueue为空,那么当前线程就会阻塞
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的prepare方法,这个方法是用来创建Looper的,我们可以看到,这里是new了一个Looper并且将这个Looper放到了之前我们说过的ThreadLocal当中。也就是当前线程的ThreadLocalMap的table数组中去。那么在new Looper时又发生了什么呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们发现,Looper中有MessageQueue和Thread这两个变量,一个消息队列,一个当前线程。由此,我们发现Thread和Looper的存储关系为,Thread中在ThreadLocalMap的table数组中存储了Looper,而Looper同时也有指向当前线程的变量mThread,MessageQueue真正存储的地方是在Looper当中。
loop方法
上面已经介绍了Looper的存储结构和创建过程,接着来看如何通过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;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
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);
}
}
...
msg.recycleUnchecked();
}
}
源代码比较长,我们这里关注几个比较重要的地方,一个是通过myLooper方法拿到当前线程的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();
}
显然这再一步说明Looper是存储在当前线程的ThreadLocalMap中的,接着看我们发现这是一个死循环,并且是通过queue.next()方法得到msg的,MessageQueue的next方法我们之前提过,如果消息队列中没有消息了就会一直阻塞。显然如果没有消息了next方法并不会返回null,那么是通过什么方法让消息循环退出呢?这里是通过Looper的quit方法来使MessageQueue调用quit方法来标记消息队列的退出状态,这样next方法就会返回null