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

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

一、Message 消息


模仿 Android 中的 Message 基本功能 , 提供 what 与 obj 变量 , 提供一个回收方法 ;


此外 , 还要指明下一个消息 , 以及是哪个 Handler 发送的该消息 ;


package kim.hsl.handler;
public class Message {
    /**
     * 消息识别码
     */
    int what;
    /**
     * 消息对象
     */
    Object obj;
    /**
     * 指向下一个消息
     */
    Message next;
    /**
     * 该 Message 使用哪个 Handler 进行发送的
     */
    Handler target;
    /**
     * 回收方法
     */
    public void recyle(){
        obj = null;
        next = null;
        target = null;
    }
}







二、Handler 消息处理者


Handler 有两个功能 :


功能一 : 发送消息到 Looper 中的 消息队列 MessageQueue 中 ;


 

/**
     * 发送消息
     * @param msg
     */
    public void sendMessage(Message msg){
        // 为消息设置发送的 Handler
        msg.target = this;
        // 向消息队列中放入要执行的消息
        mQueue.enqueueMessage(msg);
    }


功能二 : 接收 Looper 中的 loop 方法传来的 Message , 并 执行该 Message 代表的任务 ;



Handler 执行 Message 任务 , 具体的执行逻辑需要 用户实现 ; 用户创建 Handler 时 , 需要覆盖 handleMessage 方法 , 在重写的方法中处理不同的 Message 任务 ;


 

/**
     * 执行消息对应的任务
     * @param next
     */
    public void handleMessage(Message next) {
    }



Handler 初始化 :


Handler 的功能一 发送消息 , 就是向 消息队列 MessageQueue 中发送消息 , 并将消息放到 MessageQueue 中的 Message 链表队列的最后一个 ;


这就需要 Handler 持有 消息队列 MessageQueue 的引用 ,


消息队列封装在 Looper 中 , 因此需要先拿到 线程本地变量 Looper , 然后从 Looper 中获取对应的消息队列 ;



这里就需要特别注意 , 在初始化 Handler 时 , 需要用到 Looper , 如果 Looper 为空 , Handler 初始化就会失败 ;


因此在 创建 Handler 之前 , 必须先调用 Looper 的 prepare 方法 , 先将 Looper 进行初始化操作 ;



 

/**
     * 消息队列
     * 该消息队列封装在 Looper 中
     * Looper 封装在线程本地变量中
     */
    MessageQueue mQueue;
    public Handler(){
        /*
            在 Handler 中需要拿到 Looper
            进而拿到 Looper 中的 MessageQueue 消息队列
            Handler 的操作就是将 Message 放入 MessageQueue
            因此在 Handler 中需要持有 MessageQueue 消息队列的引用
            获取 Looper 时 , Looper 必须已经初始化完毕,
            也就是已经调用过 prepare 创建了 Looper 并将其放入了线程本地变量
         */
        // 获取当前线程中的 线程本地变量 Looper
        Looper looper = Looper.looper();
        // 获取封装在 Looper 中的 消息队列 MessageQueue
        mQueue = looper.mQueue;
    }


完整 Handler 代码 :


package kim.hsl.handler;
public class Handler {
    /**
     * 消息队列
     * 该消息队列封装在 Looper 中
     * Looper 封装在线程本地变量中
     */
    MessageQueue mQueue;
    public Handler(){
        /*
            在 Handler 中需要拿到 Looper
            进而拿到 Looper 中的 MessageQueue 消息队列
            Handler 的操作就是将 Message 放入 MessageQueue
            因此在 Handler 中需要持有 MessageQueue 消息队列的引用
            获取 Looper 时 , Looper 必须已经初始化完毕,
            也就是已经调用过 prepare 创建了 Looper 并将其放入了线程本地变量
         */
        // 获取当前线程中的 线程本地变量 Looper
        Looper looper = Looper.looper();
        // 获取封装在 Looper 中的 消息队列 MessageQueue
        mQueue = looper.mQueue;
    }
    /**
     * 发送消息
     * @param msg
     */
    public void sendMessage(Message msg){
        // 为消息设置发送的 Handler
        msg.target = this;
        // 向消息队列中放入要执行的消息
        mQueue.enqueueMessage(msg);
    }
    /**
     * 执行消息对应的任务
     * @param next
     */
    public void handleMessage(Message next) {
    }
}






三、MessageQueue 消息队列


Message 链表 : 消息队列 MessageQueue , 内部维护了一个 Message 链表 , 存储的时候只存储第一个 Message 即可 ;


链表插入元素 : 当 Handler 在其它线程调用 sendMessage 方法 , 将 消息 Message 放入 Looper 中的 MessageQueue 时 , 针对该链表的操作就是 , 循环获取链表的下一个元素 , 最终 获取到最后一个元素 , 最后一个元素的 next 为空 ; 将 最后一个元素的 next 设置为本次要插入的 Message , 即可完成消息存储到消息队列的操作 ;


链表元素同步 : 链表为空时 , 取出链表的操作会阻塞 , 调用的是 wait 方法 , 此时有消息加入链表后 , 需要 调用 notify 唤醒阻塞 ;


消息入队的部分代码 :


 

/**
     * 该队列是一个链表 , 因此这里只给出第一个 Message 即可
     */
    Message mMessage;
    /**
     * 将 Message 消息加入到 Message 链表中
     * @param msg
     */
    public void enqueueMessage( Message msg ){
        // 因为 该消息队列 可能会有多个线程 通过 Handler 向消息队列中添加消息
        // 因此 需要使用同步代码块包裹以下逻辑
        synchronized (this){
            if( mMessage == null ){
                mMessage = msg;
            }else{
                /*
                    如果链表不为空
                    这里需要循环查找消息队列的最后一个消息
                    将本次传入的 Message msg 参数加入到链表尾部
                 */
                Message pointer = mMessage;
                Message previous = pointer;
                for(;;){
                    // 记录上一条消息, 每次遍历都将本次遍历的记录下来
                    previous = pointer;
                    // 将 pointer 指向下一条消息
                    pointer = pointer.next;
                    // 此时如果某个 Message 的 下一个元素为空
                    // 说明该 Message 是消息队列最后一个元素
                    if(pointer == null){
                        break;
                    }
                }
                // 将本次参数传入的 Message 放到链表最后
                previous.next = msg;
            }
            notify();
        }
    }




Looper 调用 loop 方法后 , 会一直循环 , 不断地从 消息队列 MessageQueue 中取出 Message 消息 , 然后 将 Message 消息发送给对应的 Handler 执行对应的操作 ;


从 消息队列 MessageQueue 中取出消息 , 也是 取出链表表头 的操作 , 取出该链表的表头 , 然后 将表头设置成链表的第二个元素 ;


消息同步 : 如果当前链表为空 , 此时会 调用 wait 方法阻塞 , 直到消息入队时 , 链表中有了元素 , 会调用 notify 解除该阻塞 ;


/**
     * 从消息队列中获取消息
     * @return
     */
    public Message next(){
        synchronized (this){
            // 本次要获取的消息, 最后要返回到 Looper 中 loop 方法中
            Message result;
            for (;;){
                // 尝试和获取 消息队列 链表中的第一个元素
                result = mMessage;
                if(result == null){
                    // 如果当前的 Message 队列为空 , 阻塞等待 , 直到新的消息到来
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    // 如果不为空 , 说明已经获取到最终的消息 , 退出循环即可
                    break;
                }
            }
            // 处理链表逻辑 , 将表头指向下一个 Message
            mMessage = mMessage.next;
            return result;
        }
    }




消息队列完整代码 :


package kim.hsl.handler;
public class MessageQueue {
    /**
     * 该队列是一个链表 , 因此这里只给出第一个 Message 即可
     */
    Message mMessage;
    /**
     * 将 Message 消息加入到 Message 链表中
     * @param msg
     */
    public void enqueueMessage( Message msg ){
        // 因为 该消息队列 可能会有多个线程 通过 Handler 向消息队列中添加消息
        // 因此 需要使用同步代码块包裹以下逻辑
        synchronized (this){
            if( mMessage == null ){
                mMessage = msg;
            }else{
                /*
                    如果链表不为空
                    这里需要循环查找消息队列的最后一个消息
                    将本次传入的 Message msg 参数加入到链表尾部
                 */
                Message pointer = mMessage;
                Message previous = pointer;
                for(;;){
                    // 记录上一条消息, 每次遍历都将本次遍历的记录下来
                    previous = pointer;
                    // 将 pointer 指向下一条消息
                    pointer = pointer.next;
                    // 此时如果某个 Message 的 下一个元素为空
                    // 说明该 Message 是消息队列最后一个元素
                    if(pointer == null){
                        break;
                    }
                }
                // 将本次参数传入的 Message 放到链表最后
                previous.next = msg;
            }
            notify();
        }
    }
    /**
     * 从消息队列中获取消息
     * @return
     */
    public Message next(){
        synchronized (this){
            // 本次要获取的消息, 最后要返回到 Looper 中 loop 方法中
            Message result;
            for (;;){
                // 尝试和获取 消息队列 链表中的第一个元素
                result = mMessage;
                if(result == null){
                    // 如果当前的 Message 队列为空 , 阻塞等待 , 直到新的消息到来
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    // 如果不为空 , 说明已经获取到最终的消息 , 退出循环即可
                    break;
                }
            }
            // 处理链表逻辑 , 将表头指向下一个 Message
            mMessage = mMessage.next;
            return result;
        }
    }
}





目录
相关文章
|
3月前
|
安全 Android开发 开发者
【Android开发小技巧】扔掉这坑人的 Handler
【Android开发小技巧】扔掉这坑人的 Handler
40 0
|
8月前
|
Android开发
Android面试常客之Handler全解1
Android面试常客之Handler全解
|
4月前
|
消息中间件 缓存 安全
android开发,使用kotlin学习消息机制Handler
android开发,使用kotlin学习消息机制Handler
81 0
|
8月前
|
XML 消息中间件 API
Android 中handler消息机制的理解
Android 中handler消息机制的理解
48 0
|
8月前
|
XML Android开发 数据格式
Android 中简单计时器的实现方法(Handler和TimerTask)
Android 中简单计时器的实现方法(Handler和TimerTask)
329 0
|
8月前
|
消息中间件 Android开发
Android面试常客之Handler全解2
Android面试常客之Handler全解
|
10月前
|
消息中间件 存储 安全
Android 13 Handler 源码
Handler 是一套 Android 消息传递机制。   在多线程应用场景中,将子线程中需要更新 UI 的操作消息,传递到 UI 主线程,从而实现子线程通知 UI 更新最终实现异步消息处理。说白了是用于线程之间的通信。 Handler主要有4个重要类:Handler、Message、MessageQueue、Looper。
|
消息中间件 Java Android开发
【Android 异步操作】手写 Handler ( 总结 | Message | MessageQueue | Looper | Handler ) ★(二)
【Android 异步操作】手写 Handler ( 总结 | Message | MessageQueue | Looper | Handler ) ★(二)
122 0
|
消息中间件 存储 算法
【Android 异步操作】手写 Handler ( 消息队列 MessageQueue | 消息保存到链表 | 从链表中获取消息 )
【Android 异步操作】手写 Handler ( 消息队列 MessageQueue | 消息保存到链表 | 从链表中获取消息 )
213 0
|
消息中间件 存储 Java
【Android 异步操作】手写 Handler ( Message 消息 | ThreadLocal 线程本地变量 | Looper 中的消息队列 MessageQueue )
【Android 异步操作】手写 Handler ( Message 消息 | ThreadLocal 线程本地变量 | Looper 中的消息队列 MessageQueue )
146 0