Android-Looper

Android-Looper

前言:本文是针对 Looper 内部的一些分析,但涉及到的知识还可能出现在以下文章中,建议都参考一遍:


1. Looper简介

首先看一下源码中对 Looper 的注释说明:

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
40
41
42
43
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
public final class Looper {
/*
* API Implementation Note:
*
* This class contains the code required to set up and manage an event loop
* based on MessageQueue. APIs that affect the state of the queue should be
* defined on MessageQueue or Handler rather than on Looper itself. For example,
* idle handlers and sync barriers are defined on the queue whereas preparing the
* thread, looping, and quitting are defined on the looper.
*/

......
}

大致翻译:

(1)Looper 被 Thread 用于运行一个消息循环。一个线程默认不具有与之关联的消息循环(消息队列),如果想要为线程创建一个消息队列,在该线程中调用 Looper.prepare(),然后再调用 Looper.loop() 来启动消息处理循环,该循环将一直运行直到被终止。

(2)大多数情况下,和消息循环的交互是通过 Handler 完成的。

(3)以下代码是实现一个具有消息循环的线程的典型方式,通过分别调用 prepare()loop() 来创建一个初始化的 Handler 并用于和 Looper 交互:

1
2
3
4
5
6
7
8
9
10
11
12
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}

(4)Looper 类中包含某些代码,是需要通过 MessageQueue 来设置和管理事件循环的。影响消息队列的状态的 API 应该在 MessageQueue 或 Handler 中而不是在 Looper 本身定义,例如 Idle Handler 和同步障栅是在消息队列中定义的,而 Thread 的前期准备、消息循环、退出则是在 Looper 中完成的。

以上内容可能涉及到 Handler 或 Message 的内容,可以通过系列对应的文章查阅。


2. Looper初始化和实例化

Looper 类光是从名字就能看出来作用,通过之前对 Handler 的源码分析可以知道,Handler 构造时,可以手动传入一个 Looper 对象和处理消息的回调 Callback,用来与 Handler 绑定,此时这个 Handler 则会用来处理该 Looper 的消息队列中的消息,并且与该 Looper 处在同一线程,而默认构造方法则最终都调用了以下构造方法,并且内部给 Looper 传入了一个 null

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
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
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;
}

可以看到默认情况下会调用 Looper.myLooper(),再查看一下这个方法:

1
2
3
4
5
6
7
/**
* 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();
}

这个方法也就是从 ThreadLocal 中获取一个对象,ThreadLocal 的相关内容在系列文章中已有介绍,简单来说,首先需要向当前线程中 set() 一个对象,才能 get() 出来,并且 ThreadLocal 中存放的对象是和线程绑定的,不同线程只能 get() 到该线程自己 set() 进去的对象,再看一下这个 sThreadLocal 在一开始的声明:

1
2
3
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

说明存取的都是 Looper 对象。Looper 其实有两个 prepare() 方法,一个无参,另一个就是以下这个有一个布尔类型参数的,但无参的 prepare() 最终也是调用的有参这个:

1
2
3
4
5
6
7
8
9
/** 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);
}

Looper.prepare(boolean) 方法中有这么一段:

1
2
3
4
5
6
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() 方法后,会往对应的 ThreadLocal 中存放一个实例化的 Looper 对象,而 Looper.myLooper() 则是从 ThreadLocal 中取出这个对象,因此如果一个 Looper 没有先调用 prepare() 就直接使用会报错。但是为什么不直接通过 new 来生成实例对象呢?因为 Looper 的构造方法是私有的:

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

这么设计的好处我个人理解为:① Looper、Handler、Thread 这些类都是由系统进行资源管理的,用户不应具有太大的修改权限,并且 Looper 类是 final 修饰的,也不允许用户继承重写。② Looper 必须和指定的线程绑定,指定调用 prepare() 方法可以强调这一特性。③ Looper 只能和一个线程绑定,使用 ThreadLocal 管理,采用 new 的方式可能会有多线程的问题。


3. Looper总结

其实到这里 Looper 的工作原理和流程就已经可以总结了:

  1. Looper 是一个消息循环。
  2. Looper 内有成员变量 MessageQueue,并通过它循环取出、派发消息进行事件处理。
  3. Looper 无法通过构造方法实例化,而是通过 prepare() 方法,并在内部调用了 myLooper() 方法来实例化一个 Looper 对象,且会和线程绑定,通过对应线程的 ThreadLocal 来存取。
  4. Looper 所在的线程决定了 Handler 处理消息时所在的线程。实例化 Handler 时,可以自行创建某线程的 Looper 实例化对象,并将其与 Handler 绑定,则 Handler 处理的消息即来自于 Looper 所在的线程。
  5. 线程默认是不具有消息循环的,也即默认情况下一个 Thread 是不会维护 Looper 的,通过继承重写 Thread,并在其中调用 Looper.prepare() 来创建消息循环,再通过 Looper.loop() 来开启循环。

但在实际开发中其实有些比较常见的问题:

(1)平时用 Handler 处理消息时,并没有调用 Looper.prepare() 或通过其手动创建一个 Looper 对象,但依然可以正常处理消息。这是因为,Android 的主线程即 UI 线程,有一定的特殊性,整个 App 过程中仅允许存在一个 UI 线程,而 App 的主线程对应在 ActivityThread.main() 方法,其也是整个 App 的入口方法,在这个方法里面就生成了 Looper 的实例:

1
Looper.prepareMainLooper();

对应 Looper 中的源码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 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();
}
}

虽然本质上仍然是通过 prepare(boolean) 方法生成了一个成员变量 sMainLooper,但是加了类锁,并且只允许创建一次,由于 App 运行期间,主线程一直存在,因此主线程的对应的 Looper 实例对象 sMainLooper 在 App 运行期间有且仅有一个,所以在主线程中使用 Handler 无需手动创建 Looper 实例对象以及显式调用 prepare()

1
2
3
4
5
6
7
// 主线程中实例化 Handler
private Handler mainHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});

(2)即使在子线程中,也可以不显式调用 prepare() 方法,这时 Handler 的初始化方式为:

1
2
3
4
5
6
private Handler mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};

这里是 void handleMessage(Message)和(1)中使用匿名 Callback 的 boolean handleMessage(Message) 不同。

这里不需要显式调用 prepare() 的原因是给 Handler 传了一个 Looper.getMainLooper() 的参数,源码如下:

1
2
3
4
5
6
7
8
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

可以看到实际上就是返回了主线程的 sMainLooper,所以也不需要手动创建 Looper 实例对象。

(3)利用 Handler 处理消息所在线程由其实例化时传入的 Looper 对象决定 这一特性,可以衍生出主线程和子线程之间交互的方式:

  1. 子线程向主线程发送消息:在子线程实例化 Handler 时传入 Looper.getMainLooper() 参数。
  2. 主线程向子线程发送消息:在主线程实例化 Handler 时传入一个在子线程中调用过 prepare() 方法已初始化的 Looper 对象。

针对以上情形 ②,主线程开启子线程后向下执行,如果主线程发送消息的时间较早,可能子线程还没有完成对 Looper 的实例化,则会导致空指针异常,此时可以将子线程用 HandlerThread 类代替,实例化时传入一个 String 类型的线程标记名,HandlerThread 不能重写 run() 方法,当然也不需要显式调用 prepare()loop(),通过 HandlerThread.getLooper() 即可获取 Looper 实例对象。