关系概述
- 每个
Thread
中都有一个ThreadLocal.ThreadLocalMap
属性 - 这个
ThreadLocal.ThreadLocalMap
属性中保存的是以ThreadLocal
为key的类似于HashMap的哈希表,以Entry形式作为结点。 - 在线程中执行
ThreadLocal.set()
方法时,都会先获取到当前线程中的ThreadLocalMap
,然后以自身(this
)为key将数据保存进去。 -
ThreadLocal.get()
也是先获取到当前线程中的ThreadLocalMap
,然后以自身(this
)为key去取值。
这样,实际上每个线程中都保存一份数据,Looper
就是这样保存在自身当前的线程中的。这也是为什么Looper
中的sThreadLocal
是static
的,却能在不同的线程中获取到自己线程的Looper
的原因。
实际上,真正最重要的角色,是ThreadLocalMap
。
上源码
以下源码基于8.1.0,省去了一些对分析它们关系无关的代码:
Thread.java:
class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
// ...
}
ThreadLocal.java
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
//...
table[i] = new Entry(firstKey, firstValue);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
// ...
}
private void set(ThreadLocal<?> key, Object value) {
// ...
}
private Entry getEntry(ThreadLocal<?> key) {
// ...
}
}
Looper.java
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
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;
//...
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
final Looper me = myLooper();
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...
try {
msg.target.dispatchMessage(msg);
}
msg.recycleUnchecked();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public void quit() {
mQueue.quit(false);
}
}
从以上源码也可以得知为何想要在子线程中使用Handler,一定要先调用Looper.prepare()
、Looper.loop()
方法的原因:
- 消息循环是在当前的线程中进行循环的
- 每个线程都有自己的一个消息队列,而这个消息队列在
Looper
中创建并保存。 - 于是每个线程都需要保存自己的
Looper
,这个Looper
就是用ThreadLocal
保存在了当前线程中 - 这个保存的操作,就发生在
Looper.prepare()
方法中。 -
Looper.loop()
方法,开启了本线程的消息循环。
那么Handler
是如何实现在不同的线程中通信的?见我这篇:
Android Handler究竟是如何实现跨线程的?