这是我参与更文挑战的第4天,活动详情查看: 更文挑战
简介
在 Android Handler 机制中对于 Looper 在使用的过程时,是保证一个线程只有一个 Looper 的,这样通过 handler 发送的消息才能得到统一的处理。那么这个机制是如何实现的呢?通过阅读源码后可以发现是借助了一个 ThreadLocal 类来完成的。既然如此,就让我们先学习下 ThreadLocal 的原理和使用。
ThreadLocal 基本使用
首先看一下 ThreadLocal 的基本使用。
public static void main(String[] args) {
ThreadLocal<String> tl1 = new ThreadLocal<>();
tl1.set("Thread Main1");
tl1.set("Thread Main2");
new Thread(new Runnable() {
@Override
public void run() {
tl1.set("Thread New");
System.out.println(tl1.get());// Thread New
}
}).start();
System.out.println(tl1.get()); // Thread Main2
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(tl1.get()); // null
}
}).start();
}
复制代码
可以看到同一个 threadlocal 对象,可以在不同的线程中赋予不同的值,而且互不干扰。但是第二次赋值会覆盖第一次的赋值。所以我们可以推测一下,可能是通过一个 map 来保存数据的吧。
ThreadLocal 中的 set 方法
接下来就让我们看看源码吧,首先看下构造方法,你会发现构造方法的方法体是空的。那就继续看 set 方法。
// Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
// ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码
首先看到 Thread 类里面是定义有一个 ThreadLocalMap 的,这里可以理解为 map。接着看到 set 方法,首先获取当前操作的线程,获取当前线程的 map,如果 map 不为空就进行 set 操作,如果为空就新建一个 map 并赋值到当前线程中。这样当操作不同线程的时候总是会取不同线程的 map,也就保证了不同线程的值是不一样的。
ThreadLocal 中的 get 方法
接着看下 get 操作的代码。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
复制代码
可以看到也是相同的操作,先通过当前线程取出 map,如果不为空就返回值,如果为空就初始化这个 map。
如何保证 Looper 的线程唯一
看完了 ThreadLocal 的基本原理,让我们回到开头提到的 Handler 机制中的如何保证 Looper 唯一的问题上。继续跟踪下代码,看看 google 是如何实现这一个需求的。
// Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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 进行了控制,如果多次 prepare 就会报错。
这样我们就大致了解了原理和使用,后面会继续分享对 handler 机制的分析。
本文是个人的学习记录,不免对技术点有错误或不够深入的理解,还望大神小白批评指教。