对Looper源码的简单分析

概述

  • Looper在Android的消息机制中扮演着消息循环的角色,它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。

源码分析

构造方法

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建一个MessageQueue
mThread = Thread.currentThread(); //将当前线程保存起来
}

但是这个构造方法是私有的,那么我们要怎么创建一个Looper呢?

创建Looper

prepare()

通过Looper.prepare()即可为当前线程创建一个Looper

1
2
3
4
5
6
7
8
9
10
public static void prepare() {
prepare(true);
}

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)); //这里调用构造方法
}

其中sThreadLocal是一个Looper类型的ThreadLocal变量

1
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

prepareMainLooper()

Looper处理prepare方法,还提供了prepareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper用的,我们一般不要去调用该方法。

1
2
3
4
5
6
7
8
9
public static void prepareMainLooper() {
prepare(false); //创建主线程的Looper
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); //myLooper()返回当前线程的Looper对象
}
}

Looper还提供了getMainLooper方法获取到主线程的Looper

1
2
3
4
5
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

退出Looper

Looper提供了quit()和quitSafely()方法来退出一个Looper

1
2
3
public void quit() {
mQueue.quit(false);
}

1
2
3
public void quitSafely() {
mQueue.quit(true);
}

两者的区别是:

  • quit()会直接退出Looper
  • quitSafely()只是设定一个退出标记,然后把消息队列中的已有信息处理完毕后才安全地退出

Looper退出后,通过Handler发送的消息会失败,其send方法返回false。
在子线程中,如果手动创建了Looper,那么在所有的事情完成以后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于等待的状态。
而退出Looper之后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。

Loop()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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;

// 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();

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return; //唯一跳出循环的方式:queue.next()返回null
}

//...

try {
msg.target.dispatchMessage(msg); //msg.target是发送这条消息的Handler对象
//...
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}

//...

msg.recycleUnchecked(); //回收该消息
}
}
  • 当消息队列被标记位退出状态时,它的next方法就会返回null。也就是说,Looper必须退出,否则loop方法就会无限循环下去。
  • 因为MessageQueue的next方法是一个阻塞操作,当没有消息时,next方法会一直阻塞在那里,这也导致loop方法一致阻塞在那里。
  • msg.target.dispatchMessage(msg);通过这条语句,Handler发送的消息最终又交给了它的dispatchMessage方法来处理了。但由于是在创建Handler时所使用的Looper中执行的,这样就成功地将代码逻辑切换到了指定的线程中(创建Handler所在线程)执行。
-------------    本文到此结束  感谢您的阅读    -------------
0%