createMap(t, value);
}
set
方法首先获取到了当前的线程,然后获取一个map
。这个map
是以键值对形式存储内容的。如果获取的map
为空,就创建一个map
。如果不为空就塞进去值。要注意的是,这里面的key
是当前的线程,这里面的value
就是Looper
。也就是说,线程和Looper
是一一对应的。也就是很多人说的Looper
和线程绑定了,其实就是以键值对形式存进了一个map
中。没什么高大上的。你来你也行。
而这个Looper的构造方法我们也得去看一下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper
的构造方法中,可以看到它创建了一个MessageQueue
,没错就是那个被Handler
无耻使用的MessageQueue
。需要注意的一点,上面的分析中提到了prepare
方法必须调用但也只能调用一次,调用以后就会创建Looper对象,也就是说一个线程中只会创建一个Looper
对象,而一个Looper
对象也只会创建一个MessageQueue
对象。
现在我们来梳理一下这个流程哈~
首先创建一个无参数的Handler
,在这个Handler
的构造方法中又去获取Looper
对象,当然获取Looper
对象其实是为了它的MessageQueue
,Handler
巴结上了人家Looper
对象的MessageQueue
以后,发送消息的时候,把要发送的消息给了MessageQueue
,添加到了队列中。是不是感觉缺少了什么?没错,好像在这个里面Looper
的作用没体现出来,说好的分发消息呢?而且你刚刚说了得调用prepare()
方法才会创建Looper
,可我没调用过这个方法啊。那这个Looper
谁创建的?
刚才提到了,Looper
在创建的时候会被当成value
塞入到一个map
中去,这个map
是ThreadLocal
。而key
就是创建Looper
时所在的线程。也就是所谓的Looper
和线程绑定。我们一般在用的时候从没创建过Looper
,但是我们知道handle
中的回调handleMessage
方法是运行在主线程中的。Looper
的职责不就是分发消息么,也就是说Looper
对象在主线程中把消息分发给了Handler
。那么这下就明白了,在我们创建Looper
的时候,Looper
所在的线程是主线程,换言之,与这个Looper
绑定的线程就是主线程。
明白了,我这就去和面试官对线。
既然是主线程,那么大家应该知道,主线程是谁创建的?ActivityThread
类。ActivityThread
类也正是整个app的入口。以前我也很好奇,既然Android
是用Java
写的,按理说Java
不应该是有个什么main
方法么?怎么我写Android
没用过这个main
方法呢?其实呢,在ActivityThread
中就有这个main
方法,它是程序的入口,也就说当你点开app
以后,首先会进入到这个main
方法中,然后做了一大堆事情,这里就不分析了。你只需要知道,这个main
方法才是真正的入口。
那我们来看看这个main方法到底干了什么事情:
//ActivityThread.java
//省略部分代码
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “ActivityThreadMain”);
Process.setArgV0("");
//1 敲黑板,划重点,就是这一句!
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//2 敲黑板,划重点,这一句!
Looper.loop();
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}
这段代码是不是很符合我们平常写的java程序呢?熟悉的main方法又回来了。main方法中可以看到,它调用了Looper的prepareMainLooper
方法:
public static void prepareMainLooper() {
//设置不允许退出的Looper
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(“The main Looper has already been prepared.”);
}
sMainLooper = myLooper();
}
}
可以看到注释1,这个方法最终还是调用了Looper
的prepare
方法,这个方法干嘛的?创建Looper
并且把它和当前线程一起塞进map
中的啊。当前线程是哪个线程?主线程啊!
一切到这里就真相大白了,在APP
启动的时候,入口方法中已经自动帮我们创建好了Looper
,并且也自动的帮我们和主线程绑定了。也就是说我们平常用的Handler
中的Looper
就是主线程中创建的这个Looper
。细心的小伙伴应该会发现,这个prepareMainLooper
方法你是不能调用的。为啥?因为这个方法在入口的时候执行了一次,所以里面的sMainLooper
不为Null了,如果你在调用一次,不就要抛异常了么~
现在Looper
也有了,Looper
的MessageQueue
也有了。接下来该分发消息了吧?我Handler
发送消息可是已经很久过去了,你这里分析一大通,我还干不干活了?
好,我们现在先假设一个场景。
你买了一个快递,你知道迟早会给你送到,但是不确定到底什么时候才会送到。你想早点拿到快递应该怎么做?
你会不停的问快递公司,我的快递到哪了,到哪了。当然,现实中一般都是等快递员打电话才去拿快递~问题在于,这是程序。
Looper
虽说要分发消息,但是它又不知道你什么时候会发送消息,只能开启一个死循环,不断的尝试从队列中拿数据。这个死循环在哪里开始的?没错就是注释2处,Looper.loop()
开启了一个死循环,然后不断的尝试去队列中拿消息。
// Looper.java
public static void loop() {
//拿到当前线程的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}
//拿到Looper的消息队列
final MessageQueue queue = me.mQueue;
// 省略一些代码…
//1 这里开启了死循环
for (;😉 {
<