四、Looper 循环者
Looper 是 线程本地变量 , 在每个线程中 , 可以通过线程调用 ThreadLocal 变量的 get 方法获取该线程对应的对象副本 , 调用 ThreadLocal 变量的 set 方法 , 设置该线程对应类型的对象副本 ;
Looper 调用 prepare 方法进行初始化 , 在该方法中处理 线程本地变量的先关初始化与设置 ,
如果之前已经初始化过 , 本次调用 prepare 方法是第二次调用 , 则会 抛出异常 ,
如果之前没有初始化过 , 那么创建一个 Looper , 然后调用线程本地变量 ThreadLocal 的 set 方法 , 将该 Looper 对象设置成线程本地变量 ;
/** * 一个线程只能有一个 Looper * 使用 ThreadLocal 来保存该 Looper * 是线程内部存储类 , 只能本线程才可以得到存储的数据 ; */ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>(); /** * 准备 Looper 方法 */ public static void prepare(){ System.out.println("prepare 创建 Looper "); // 先进行判断 , 如果当前线程已经有了 Looper , 那就抛出异常 if(sThreadLocal.get() != null){ throw new RuntimeException("当前线程已存在 Looper"); } // 如果不存在 Looper , 就创建一个 Looper sThreadLocal.set(new Looper()); }
在 Looper 线程中 , 最后一句代码肯定是 Looper.loop() , 执行该方法后 , 就开启了一个无限循环 ,
不断从 消息队列 MessageQueue 中获取消息 , 然后发送给该 消息 Message 对应的 Handler ,
哪个 Handler 发送的消息 , 就将消息在送回给哪个 Handler ;
消息同步 : 当 消息队列 MessageQueue 为空时 , 无法从消息队列中获取数据 , 此时线程会 阻塞 , 直到有新的消息到来后 , 解除阻塞 ;
Looper 循环遍历消息队列部分代码 :
/** * 不断从 消息队列 MessageQueue 中取出 Message 消息执行 */ public static void loop(){ System.out.println("开始无限循环获取 Message"); // 获取当前线程的 Looper Looper looper = Looper.looper(); // 从当前线程的 Looper 获取 消息队列 MessageQueue MessageQueue messageQueue = looper.mQueue; // 不断从 消息队列中获取 消息 , 分发到发送消息的 Handler 中执行 for(;;){ // 获取消息队列中的第一个消息 Message next = messageQueue.next(); // 分发到发送该消息的 Handler 中执行 next.target.handleMessage(next); } }
完整 Looper 代码 :
package kim.hsl.handler; public class Looper { /** * 一个线程只能有一个 Looper * 使用 ThreadLocal 来保存该 Looper * 是线程内部存储类 , 只能本线程才可以得到存储的数据 ; */ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>(); /** * 消息队列 */ public MessageQueue mQueue; /** * Looper 构造函数 */ private Looper(){ mQueue = new MessageQueue(); } /** * 获取当前线程对应的 Looper * @return */ public static Looper looper(){ return sThreadLocal.get(); } /** * 准备 Looper 方法 */ public static void prepare(){ System.out.println("prepare 创建 Looper "); // 先进行判断 , 如果当前线程已经有了 Looper , 那就抛出异常 if(sThreadLocal.get() != null){ throw new RuntimeException("当前线程已存在 Looper"); } // 如果不存在 Looper , 就创建一个 Looper sThreadLocal.set(new Looper()); } /** * 不断从 消息队列 MessageQueue 中取出 Message 消息执行 */ public static void loop(){ System.out.println("开始无限循环获取 Message"); // 获取当前线程的 Looper Looper looper = Looper.looper(); // 从当前线程的 Looper 获取 消息队列 MessageQueue MessageQueue messageQueue = looper.mQueue; // 不断从 消息队列中获取 消息 , 分发到发送消息的 Handler 中执行 for(;;){ // 获取消息队列中的第一个消息 Message next = messageQueue.next(); // 分发到发送该消息的 Handler 中执行 next.target.handleMessage(next); } } }
五、关于 Looper 线程本地变量的说明
ThreadLocal 作用是 保存线程私有变量 ;
使用 ThreadLocal 维护一个变量时 , 每个使用该 ThreadLocal 线程本地变量 的线程 , 都会 被分配一个独立的变量副本 ,
每个线程 只 可以 改变本线程内的 变量副本 , 即 ThreadLocal 线程本地变量 ;
1 . ThreadLocal 定义 :
/** * 一个线程只能有一个 Looper * 使用 ThreadLocal 来保存该 Looper * 该变量是线程内部存储类 , 只能本线程才可以得到存储的数据 ; */ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
2 . ThreadLocal 变量获取 : 调用 ThreadLocal 变量的 get() 方法 , 可以获取该 ThreadLocal 线程本地变量 ;
在 ThreadLocalMap map = getMap(t) 中 , 获取的 ThreadLocalMap 与 Java 中的 Map 集合没有任何关联 , 该类就是为了保存 线程本地变量而在 ThreadLocal 中设置的内部类 ; 在该 ThreadLocalMap 内部类中 , 通过 key 键 , 获取对应 value 值 ;
public class ThreadLocal<T> { /** * 返回 该线程本地变量的 当前线程的变量副本. * 如果 该线程中对应的 变量没有值, 应该首先初始化该变量值 * * @return 返回当前线程的线程本地变量值 */ public T get() { // 首先通过 Thread 拿到当前的线程 Thread t = Thread.currentThread(); // 通过当前线程 , 获取当前线程的 ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { // 通过 key 获取指定的 value ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } }
3 . ThreadLocal 变量设置 : 调用 ThreadLocal 的 put() 方法 , 可以设置 线程本地变量 ;
4 . Looper 中关于 线程本地变量 的设置 : 在 Looper 中涉及到了 线程本地变量 的设置 ,
Looper 要求每个线程只能保持一个 , 并且各个线程之间的 Looper 相互独立 , 没有任何关联 ;
这就需要 将 Looper 定义成线程本地变量 ;
public class Looper { /** * 一个线程只能有一个 Looper * 使用 ThreadLocal 来保存该 Looper * 是线程内部存储类 , 只能本线程才可以得到存储的数据 ; */ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>(); /** * 准备 Looper 方法 * 其中 * 使用了 sThreadLocal.get() 获取线程本地变量 * 使用了 sThreadLocal.set(new Looper()) 设置线程本地变量 */ public static void prepare(){ // 先进行判断 , 如果当前线程已经有了 Looper , 那就抛出异常 if(sThreadLocal.get() != null){ throw new RuntimeException("当前线程已存在 Looper"); } // 如果不存在 Looper , 就创建一个 Looper sThreadLocal.set(new Looper()); }