Looper 里面是如何使用 ThreadLocal 的?

这是我参与更文挑战的第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 机制的分析。

本文是个人的学习记录,不免对技术点有错误或不够深入的理解,还望大神小白批评指教。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享