【Android 异步操作】手写 Handler ( 总结 | Message | MessageQueue | Looper | Handler ) ★(二)

简介: 【Android 异步操作】手写 Handler ( 总结 | Message | MessageQueue | Looper | Handler ) ★(二)

四、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());
    }


目录
相关文章
|
消息中间件 Android开发
Android Handler的使用方式以及其机制的简单介绍
Handler 是 Android 中实现线程间通信的重要机制,可传递任意两线程数据。常用场景包括子线程向主线程(UI 线程)传递结果,以及主线程向子线程发送消息。其核心涉及四个类:Handler(发送/接收消息)、Message(消息载体)、MessageQueue(消息队列)和 Looper(消息循环泵)。基本流程为:Handler 发送 Message 至 MessageQueue,Looper 从队列中按 FIFO 取出并处理。
432 0
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
9月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1727 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
9月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1109 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
9月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
1192 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
10月前
|
开发工具 Android开发
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
926 11
X Android SDK file not found: adb.安卓开发常见问题-Android SDK 缺少 `adb`(Android Debug Bridge)-优雅草卓伊凡
|
9月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
429 0
|
10月前
|
Java 开发工具 Maven
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
【01】完整的安卓二次商业实战-详细的初级步骤同步项目和gradle配置以及开发思路-优雅草伊凡
1365 6
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
5644 77

热门文章

最新文章