乐趣区

一个线程可以有几个Looper?几个Handler?从Looper.prepare()来看看关于Looper的一些问题

前言
之前我有篇文章里面写到了 Android 的消息机制,Handler 发送消息的一些原理。链接如下:
从 Handler.post(Runnable r) 再一次梳理 Android 的消息机制(以及 handler 的内存泄露)
在消息机制里面,有一个非常重要的东西,那就是 Looper,Looper 的作用主要是从消息队列里面取出消息交给 Handler 处理,不过不仅限于此,在这里面还有很多东西值得我们去源码看一看:
1. 从 Looper.prepare() 开始
要在一个线程里面处理消息,代码如下:
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();
}
首先就必须要先调用 Looper.prepare(),那这个方法做了些什么呢:
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.set(new Looper(quitAllowed)),首先来看看 sThreadLocal
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal:代表了一个线程局部的变量,每条线程都只能看到自己的值,并不会意识到其它的线程中也存在该变量。
在这里 ThreadLocal 的作用是保证了每个线程都有各自的 Looper
上面的判断也说明了一个问题:一个线程只能有一个 Looper
接下来看看创建 Looper 实例的方法 new Looper(quitAllowed):
final MessageQueue mQueue;
final Thread mThread;

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在构造方法里,初始化了 MessageQueue 和代表当前线程的属性 mThread,关于 MessageQueue 可以看看文章开头的链接,里面有详细的代码解析,这里就不赘述了。
调用 Looper.prepare() 其实就是利用 ThreadLocal 为当前的线程创建了一个独立的 Looper,这其中包含了一个消息队列
2. 创建 Handler->new Handler()
在为当前线程创建了 Looper 之后,就可以创建 Handler 来处理消息了,这里可以解决我们一个疑问:
Handler 是怎么跟 Looper 关联上的?
// 全局变量
final Looper mLooper;
final MessageQueue mQueue;

public Handler(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 that has not called Looper.prepare()”);
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在 Handler 中有两个全局变量 mLooper 和 mQueue 代表当前 Handler 关联的 Looper 和消息队列,并在构造函数中进行了初始化,重要的就是调用了:Looper.myLooper():
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
其实还是调用的线程局部变量 sThreadLocal,获取当前线程的 Looper,这里需要注意的是,如果当前线程没有关联的 Looper,这个方法会返回 null。
注意:Handler 在哪个线程创建的,就跟哪个线程的 Looper 关联,也可以在 Handler 的构造方法中传入指定的 Looper
3.Looper.loop() 循环读取消息
这个方法也在之前的文章里讲到过,核心就是一个死循环,从 MessageQueue 里面取消息出来交给 Handler 来处理。
线程消息机制的原理
看了源码之后,我们就知道了为啥在线程中需要处理消息,必须要经过以上三个步骤,且顺序不可更改
1.Looper.prepare():为当前线程准备消息队列 2.Handler 默认构造方法跟当前线程中的 Looper 产生关联
3.Looper.loop() 开启循环取消息

衍生问题
一个线程可以有几个 Looper?
这个问题在刚才已经探讨了,只能有一个,不然调用 Looper.prepare() 会抛出运行时异常,提示“Only one Looper may be created per thread”
一个线程可以有几个 Handler
可以创建无数个 Handler,但是他们使用的消息队列都是同一个,也就是同一个 Looper
同一个 Looper 是怎么区分不同的 Handler 的,换句话说,不同的 Handler 是怎么做到处理自己发出的消息的
这个问题就要来到 Handler 的 sendMessage 方法里面了,具体的流程这里不详说了,最后来到了这个方法
Handler.enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到这一句 msg.target = this;,这里就是将当前的 Handler 赋值给 Message 对象,这样在处理消息的时候通过 msg.target 就可以区分开不同的 Handler 了。处理的方法在 Looper.loop 中:
Looper.loop()

msg.target.dispatchMessage(msg);

顺便提一句,在 Message 的 obtain 的各种重载方法里面也有对 target 的赋值

退出移动版