Android Handler 机制,自己写一个 Handler 机制?

1,041 阅读7分钟

为什么需要写一个 Handler 机制?

写一个能用的 Handler 机制是我们的目的,但却不是我们想要得到的东西,最重要的是在完成这个目的地途中,我们对 Handler 设计的思考和理解!就好比旅行的最美好的事物是路途上的所见所闻所思所想。完成之后我们再去研究 Android 系统提供的 Handler 机制,会更加的事半功陪!

自己写一个 Handler 机制难不难?

之前在和同事讨论一个定时任务需求的时候,我们各自总结了一下实现定时任务的几种方式,就说到了使用 Handler 来做定时任务处理,蓦然地想到一个问题,Handler 中的定时任务是如何做到定时的呢?当时脑海中就飘过 Message.next() 函数获取 Message 消息时调用的 MessageQueue.nativePollOnce() 函数,模糊的记得里面好像是传入了一个时间参数,并不是百分百的肯定。

对基本概念模棱两可不是一个好习惯。在日常开发工作中对一个重要概念模棱两可,容易使得 bug 频出,花费大量的时间去解决。既然如此那么我们何不集中花费一些时间,彻底搞懂哪些模棱两可的概念,以避免后续不断的为此问题来花费时间呢。

下面我将按照如下的步骤来阐述我所理解的 Handler 消息处理机制,文章将分为两个部分,第一部分为思考设计一个自制 Handler 处理,第二部分是基于源码分析 Handler 机制和思考面试遇到的 Hanler 问题与冷门 Handler 问题。

  1. 首先明确 Handler 机制的本质是一个有序的消息处理器,没啥黑科技,利用伪代码表达 Handler 的源码构成和设计思路。
  2. 将源码精简压缩为伪代码,分析一次 Message 处理的过程。
  3. 利用伪代码详细讲解组成部分(Handler / Looper / MesaageQueue / Message)是如何完成各自的工作以及协同作业地。
  4. 总结列举 Handler 机制重要的概念和面试问题。

同时,我还是推荐想要了解 Handler 机制的同学去阅读 Handler 源码,Handler 机制相关源码还是比较简单的阅读难度不大,整体的学习节奏以阅读源码为主,网上的文章(当然包括这篇)为辅,自己独立思考来进行学习。

以下的用到的源码版本为 SDK 29!!!!

Handler 机制的本质是一个有序的消息处理器

明白本质,Handler 没啥神秘的东西

首先我们要明确 Handler 消息处理机制的本质就绪一个有序的消息处理器,其目的是为了实现 UI 的更新只能由单一的线程来执行即单线程更新 UI 模式,避免多线程更新 UI 引发问题。

清楚了这一点之后,我们就来想象一下一个有序的消息处理器应该有哪些部分组成呢?如果让你来设计一个单一的有序的线程处理机制你会怎么设计呢?

  1. 一个负责发送消息的。
  2. 一个存储消息的容器,消息发出了总的有个容器装,不然那发送的消息多了没来得及处理咋办?
  3. 负责不断的从容器中取消息的。
  4. 负责处理消息的。
  5. 消息本身的载体,用于规定消息的格式,不能什么类型的消息都能发送与处理。

最后呢应该是形成如下的一个模式:消息单向流动,一次处理一条消息,容器内的消息有序排列。

伪代码自制 Handler 机制和设计思路

基于上面的设计思路呢,我们利用伪代码并结合 Android Handler 实际机制来实现一个单一的有序的消息处理机制!

  1. 消息载体

    这个我们使用一个 Message 对象作为消息载体来承载消息,那么其类的伪代码如下:

    public class Message {
    		public String content;// 消息的内容
    		public int id;// 消息的识别 id,便于处理分别嘛。 
    }
    
  2. 消息容器,直接使用队列结构存储即可,单一有序全都有了。

    public class MessageQueue {
        private Message mMessage;
    
        // synchronized 简单粗暴处理线程安全
        public void addMessage(Message msg){
            if (mMessage != null){
                mMessage.next = msg;
            } else {
                mMessage = msg;
            }
        }
        // 获取队头消息
        public Message next(){
            if (mMessage == null) return null;
            Message current = mMessage;
            mMessage = current.next;
            return	current;
        }
    }
    
  3. 获取消息并处理,还要不断的获取怎么处理?简单,一个死循环就能搞定了么。另外,这里我们需要处理如何让消息都在目标线程中处理,实现单线程处理消息?

    public class Looper {
    		private MessageQueue messageQueue;
    		
    		public void loop(){
    				for(;;){
     					Message msg = messageQueue.next();
    				}		
    		}
    }
    
  4. 获取到的消息如何处理呢?以及一个最重要的目标,如何保证所有的消息处理都在目标线程中执行呢?

    设计思路

    ​ 首先是第一个目标,如何处理?这简单直接利用一个 Handler 类,里面包含处理 Message 的函数即可。那么第二个目标又如何实现呢?

    ​ 既然 Handler 作为消息的处理类,那么 Handler 类的实例化对象的消息处理肯定的位于目标线程中,其次 Lopper 是需要直接依赖 MessageQueue 的,直接将 MessageQueue 的初始化交给 Looper 就好,最后呢就只需要思考 Looper 的设计了。

    ​ 首先 Looper 中有一个死循环操作,那么单个线程中只能有一个 Looper (有多个也没啥用),然后想想 Hnadler 和 Looper 之间的关系,Looper 获取消息而 Handler 处理消息,两者能不能处于不同的线程呢?,除此之外也没啥好设计的了,退出机制?要想的话加一个就行,没消息就退出,或者延时退出都行。

    处理发送消息

    ​ 发送消息的目的就是向 MessageQueue 中添加一条 msg,这里我们需要考虑的问题就是如何处理好多线程之间的交互问题,我们在这里使用 Java 线程的 wait / notify 机制实现(早期 android 系统中也是利用这个机制实现),Looper 在死循环获取消息时进行加锁,获取到的消息为空时就使用 wait() 进行等待,当有消息添加时notify()唤醒 Looper 线程处理消息

    ​ 至此,Handler 和 Looper 的重新改造的模样如下:

    // Handler 
    public class Handler {
    		/**
    		* 处理 Message 的方法,需要继承者实现。
    		**/
    		public void handlerMessage(Message msg){
    			// 根据 msg 的 id 来分别处理不同的 msg
    		}
    }
    
    // 添加功能之后的 Looper 
    public class Looper {
        private MessageQueue messageQueue;
        public Handler mHandler;// 处理消息的
        
        private Object lock = new Object();// 线程锁,避免死循环消耗资源,控制发送消息线程和 looper 线程安全
    
        public Looper(){
            messageQueue = new MessageQueue();
        }
    
        /**
         * 给 Looper 添加一个 Handler
         **/
        public void setHandler(Handler handler){
            this.mHandler = handler;
        }
    
        public void addMessage(Message msg){
            synchronized (lock){
                messageQueue.addMessage(msg);
                // 消息加入,唤醒 loop 运行线程
                lock.notify();
            }
        }
    
        public void loop(){
            for(;;){
                synchronized (lock){
                    Message msg = messageQueue.next();
                    if(mHandler != null && msg != null){
                        mHandler.handlerMessage(msg);
                    } else {
                        // 等待
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    
  5. 使用

        public static void main(String[] args){
            // 创建 Looper
            Looper looper = new Looper();
            // 创建 Handler 实例,处理消息
            Handler handler = new Handler(){
                @Override
                public void handlerMessage(Message msg) {
                    System.out.println("ID:" + msg.id + " Content: " + msg.content);
                }
            };
            // 给 Looper 设置 Handler
            looper.setHandler(handler);
    
            // 启动子线程,每隔一段时间发送一条消息
            Thread thread = new Thread(new Runnable() {
                int id = 0;
                @Override
                public void run() {
                    for (;;){
                        // 每次先睡 3 秒,控制频率和线程安全
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Message msg = new Message();
                        msg.id = id;
                        msg.content = "内容编码" + id;
                        // 发送消息
                        looper.addMessage(msg);
                        id++;
                    }
                }
            });
            thread.start();
            // 启动循环查询处理消息
            looper.loop();
        }
    

    结果完美

小结

至此我们已经完成了一个近似于 Android 系统中 Handler 机制的消息处理机制,在下篇文章中我将从源码分析 Android Handler 机制以及思考面试中遇到的 Handler 相关的问题与一些冷门问题(唉,为啥面试不问本质净问些偏僻问题呢?)。然后我们在完善一下开头的哪张分析图片。

最后,学习Android系统源码相关问题还是应该基于源码,站在巨人的肩膀上独立思考🤔