本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

一切从 Android 的 Handler 讲起(三):Looper 的唯一性——ThreadLocal

肥柴在起始篇:一切从 Android 的 Handler 讲起(一):Handler 工作机制中提及到,一个 Looper 对应一个线程,也就是说,Looper 在线程上具备唯一性。

那么问题来了,Handler 底层是如何来实现 Looper 的唯一性的呢?

肥柴决定从 Looper 的源码入手,我们知道一个线程的 Looper 的创建是调用 Looper 的 prepare 方法,那我们就从这里开始下手。

    /** Looper.class */

    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    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));
    }

可以很清楚的看到,prepare 方法中,通过一个 ThreadLocal 来保证了 Looper 在线程上的唯一性。

我们再来看看 Looper 的构造函数,可以发现,Looper 构造函数内创建了对应的 MessageQueue,也就是我们常说的,一个线程只有一个 Looper,MessageQueue 在 Looper 对应的线程内

    /** Looper.class */

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

ThreadLocal 是提供了在与当前线程绑定的局部变量,不同线程都会取到不同的值的工具类,这在一些并发、变量传递等场景下非常好用。肥柴继续发出疑问:那 ThreadLocal 内部又是通过什么样的原理来做到不同线程取不同的值的呢?

我们从 ThreadLocal 的 get 和 set 源码入手来了解其原理。

    /** ThreadLocal.class */

    public T get() {
        Thread t = Thread.currentThread();
        /** 注释1 */
        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();
    }

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    ThreadLocalMap getMap(Thread t) {
        /** 注释2 */
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        /** 注释3 */
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

无论是 ThreadLocal 的 get 方法还是 set 方法,其原理思路就是:

1、获取当前 Thread;

2、获取当前 Thread 对应的 ThreadLocalMap(注释 1),包括了设置(注释 2)、先创建后设置(注释 3)两种;

3、以 ThreadLocal 为 Key,需要保存的 value 为 ThreadLocalMap 的 value,保存到对应的 ThreadLocalMap(一个非常类似于 HashMap 的结构)中或者从其中取出;

基于以上肥柴可以得知 ThreadLocal 的大致工作原理:每个线程都有一个 ThreadLocalMap 结构,其中就保存着当前线程所持有的所有 ThreadLocal。ThreadLocal 本身只是一个引用,没有直接保存值,值是保存在 ThreadLocalMap 中,ThreadLocal 作为 key,值作为 value。可以用下面的图来概括。

肥柴总结

1、Looper 在线程上具备唯一性,其原因在于 ThreadLocal;

2、MessageQueue 也具备线程上的唯一性,其原因在于 Looper 构造时创建了对应的 MessageQueue;

3、Looper.prepare() 方法只能调用一次

- - - - - Looper 的唯一性篇完 - - - - -

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐