1.Message是Handler接收和处理消息的对象。
2.每一个线程只能有一个Looper。
3.Looper中有一个方法可以读取MessageQueue消息队列中的消息。
4.Looper会将消息取出交给Handler来进行消息的处理。
5.消息队列MessageQueue采用先进先出的方式来管理我们的Message。
6.MessageQueue对象的创建在Looper的构造方法中。
7.Handler的作用有两个,一个是发送消息,一个是处理消息。
8.由于Handler发送消息必须发送到指定的MessageQueue消息队列当中,而消息队列MessageQueue又由Looper进行创建和管理。所以如果你想让Handler正常工作,就要在当前线程有一个Looper对象。
9.Handler整体工作机制
首先要在主线程创建的时候为主线程创建一个Looper。而在创建Looper对象的同时,又会在Looper内部创建一个消息队列MessageQueue这个对象。而创建Handler的时候会取出当前线程的Looper。然后通过这个Looper不断地去轮询消息队列中的Message。然后Handler在子线程中发送消息其实就是在消息队列MessageQueue中添加一条Message。最后通过Looper当中的消息循环取得消息队列中的Message,然后交给我们的Handler去进行处理。
10.Handler的构造方法中传入一个Callback和一个布尔类型的值。
11.Handler构造方法内部会创建一个Looper对象
mLooper = Looper.myLooper();
12.Handler构造方法解读
Handler构造方法源码如下
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;
}
<1>Handler构造方法中传入一个Callback和一个布尔类型的值。
<2>Handler构造方法内部会创建一个Looper对象。
mLooper = Looper.myLooper();
<3>Handler构造方法中,获取到Looper对象后,会对Looper对象进行判断
if (mLooper == null)
如果为空则会抛出异常并说明Looper对象为空是因为没有调用Looper.prepare()方法。
<4>Handler构造方法中,将Looper的消息队列取出赋值给Handler中一个消息队列的对象
mQueue = mLooper.mQueue;
<5>Handler构造方法中,Handler、Looper、MessageQueue三者捆绑逻辑如下:
Handler构造方法中,通过Looper.myLooper()获取与Handler相关的Looper对象,然后通过该Looper对象获取到Looper中的MessageQueue。
13.Handler中的sendEmptyMessage,sendEmptyMessageDelayed这些方法最终都会走到enqueueMessage方法内部,而该方法实质就是将Handler发送的消息添加到消息队列当中。
14.在enqueueMessage方法内部有如下代码
msg.target = this;
这个this就是我们的Handler对象。这个操作就是将handler对象绑定到Message内部的一个target变量当中了。
15.消息对列包括两种操作:插入和读取。而读取操作本身也会伴随删除操作。
16.在MessageQueue中,有如下两个方法:
- enqueueMessage:向消息队列中插入一条消息。
- next:从队列中读取并删除消息。
17.MessageQueue内部通过单链表数据结构来维护消息列表的。因为单链表在插入和删除上比队列有优势。
18.Looper的创建在Looper的perpare()方法中进行。
19.Looper类中通过sThreadLocal容器存放Looper对象本身。
20.使用sThreadLocal容器可以保证每一个线程获取到的Looper都是唯一的。
21.sThreadLocal如何保证Looper的唯一性?
sThreadLocal在很多地方被叫做线程本地变量,它能为每个变量在每个线程都创建一个副本。每个线程都可以访问自己内部副本的变量,这样就不用每一个线程去取别的线程的变量,而导致不同线程的变量是不一致的这种现象。所以它最大的好处就是:每一个线程它的变量是独立的。你取该线程就是相应的变量,而不会改变其它线程变量的值。
22.Looper.prepare()源码分析
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方法中,我们先判断了sThreadLocal中是否可以获取到Looper,如果可以则抛出异常。所以当我们Looper重复调用prepare()方法会抛异常。如果为空就要new一个Looper对象放入sThreadLocal中。
23.Looper的构造方法源码分析
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
首先Looper的构造方法是私有的,所以不能直接进行创建。
Looper在创建时,MessageQueue也同时被创建了。
24.Looper.myLooper()源码分析
在第12项中我们了解到,Handler构造方法中,通过mLooper = Looper.myLooper();语句获取到对应的Looper对象。这里我们就看一下Looper.myLooper()源码,源码如下
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到,创建Looper就是通过sThreadLocal这个容器获取。sThreadLocal这个容器是线程所持有的,所以说它能保证我们这个Looper是和每一个其它的线程所独立开来的。通过调用sThreadLocal的get()方法就可以获取到相应的Looper对象。
25.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);
......
} catch (Exception exception) {
......
} finally {
......
}
......
}
}
<1>首先它会使用myLooper()方法获取一个Looper对象。
final Looper me = myLooper();
<2>接着会判断获取到的Looper对象是否为空,为空说明没有调用Looper.prepare()方法,抛出一个异常。
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
<3>接着获取到Looper中的消息队列。
final MessageQueue queue = me.mQueue;
<4>接下来,我们开启一个死循环
for (;;) {
......
}
在死循环中,取出消息队列中的下一条消息。
Message msg = queue.next(); // might block
如果msg为空,直接return
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
如果不为空,则会调用
msg.target.dispatchMessage(msg);
target就是我们的Handler对象。即如果消息不为空,则将消息交给Handler处理。
26.Handler的dispatchMessage方法分析
源码如下
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
<1>首先它会判断msg.callback是否为空,如果不为空则调用handleCallback(msg);方法处理消息
if (msg.callback != null) {
handleCallback(msg);
}
<2>这里的callback就是我们熟悉的Runnable
<3>handleCallback源码如下
private static void handleCallback(Message message) {
message.callback.run();
}
可以看到就是调用内部传进来的Runnable的run方法。表示在子线程做一些操作。
<4>当msg.callback为空的时候,会调用mCallback的handleMessage方法,这个就是我们在主线程中创建Handler时重写的那个handleMessage方法。
27.Handler整体流程回顾
<1>创建一个Looper对象
Looper的作用是不断从MessageQueue中获取Message
<2>Message哪里来的?
Hander发送的
<3>Handler有两个作用,一个是将消息发送,插入到MessageQueue中,另一个是Looper不断轮询取出消息后,将消息交给Handler,由Handler进行处理。
28.Handler总结
<1>Looper类主要是为每一个线程开启的单独的消息循环,主线程可以不用我们操作Looper,因为主线程已经自动为我们启动Looper了。但子线程要在Handler创建前调用Looper.prepare(),在Handler创建后调用Looper.loop()。
<2>Handler可以看做是一个接口,用来向指定的Looper中的MessageQueue发送消息。
<3>非主线程中直接new Handler()是不可以的,必须在该线程中创建好Looper。