Android Framework | Handler 消息机制

作者: 程序老秃子 | 来源:发表于2022-09-16 19:57 被阅读0次

    引言

    消息机制Android 中重要的线程间通信手段;它的存在可以让一个线程通知另一个线程去工作

    众所周知 Handler 在 Android 中的地位非常重要, 从处理异步线程任务队列的 HandlerThread 到从子线程与UI线程的通信, 再到 ActivityThread四大组件sendMessage 调度, 再到进程间通信与之关联 Messenger 可以说是从头到尾贯穿的整个 Android 系统枝枝蔓蔓;所以说搞明白,搞懂 Handler 消息运行机制与原理至关重要

    下面我们来看一下 Handler 的工作流程图

    我们先从 Handler 源码看起吧,下一步看 MessageQueue 源码,最后看 Looper 源码

    Handler 工作原理

    Handler的主要工作是发送消息和接受消息

    消息发送过程

    消息的发送我们可以通过一系列 sendpost 方法
    来看一下这些 sendpost 方法源码吧

        public final boolean sendEmptyMessage(int what)
        {
            return sendEmptyMessageDelayed(what, 0);
        }
    
        public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
            Message msg = Message.obtain();
            msg.what = what;
            return sendMessageDelayed(msg, delayMillis);
        }
        public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    
        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
        public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
        public final boolean postDelayed(Runnable r, long delayMillis)
        {
            return sendMessageDelayed(getPostMessage(r), delayMillis);
        }
    
        public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
        {
            return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
        }
    
        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    
        public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
            Message msg = Message.obtain();
            msg.what = what;
            return sendMessageAtTime(msg, uptimeMillis);
        }
    
        public final boolean sendMessageAtFrontOfQueue(Message msg) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, 0);
        }
    
        public final boolean postAtFrontOfQueue(Runnable r)
        {
            return sendMessageAtFrontOfQueue(getPostMessage(r));
        }
    

    Handler 的send和post方法是很多,但是最终都调用了enqueueMessage方法。我们来看一下enqueueMessage方法源码

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    msg.target 就是当前的 Handler,queue.enqueueMessage(msg, uptimeMillis);就是将消息插入消息队列;发送消息的过程结束了,我们来看一下接收消息过程

    消息接收过程

    当有消息时 Looper 调用 MessageQueue 的 next() 方法取出消息,然后调用 msg.target.dispatchMessage(msg) 方法, msg.target 就是 Handler,上面已说过,这个方法就是 Handler 消息的处理,我们看一下 Handler 的 dispatchMessage 源码

        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    Handler 构造方法

    先看默认构造方法吧

     public Handler() {
         this(null, false);
    }
    
    

    会调用下面的构造方法

    public Handler(Callback callback, boolean async) {
        ....省略
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    

    如果当前线程没有 Looper 就会抛出 Can't create handler inside thread that has not called Looper.prepare() 异常,这就是为什么在没有 Looper 的子线程中创建 Handler 会报这个异常原因了

    Handler 还有一种特殊的构造函数,通过 Looper 来构造一个 Handler,构造函数源码如下

    public Handler(Looper looper) {
        this(looper, null, false);
    }
    

    为什么要有Android消息机制?

    我们知道 Handler 的主要作用是将一个任务切换到某个指定的线程去执行,比如 Android 规定访问 UI 只能在主线程中进行,在大多数情况下如果在子线程中访问那么程序会抛异常

    void checkThread(){
            if(mThread != Thread.currentThread()){  
                throw new CalledFromWrongThreadException(  
                    "Only the original thread that created a view hierarchy can touch its views.");  
            }  
    }
    

    为什么系统不允许在子线程中访问UI呢?

    这是因为 Android 的 UI 控件不是线程安全的,如果在多线程中访问 UI 控件则会导致不可预期的状态

    那为什么不对 UI 控件访问加锁呢?

    缺点有两个:首先加锁会让UI控件的访问的逻辑变的复杂;其次,锁机制会降低UI的访问效率

    那我们不用线程来操作不就行了吗?

    但这是不可能的,因为 Android 的主线程不能执行耗时操作,否则会出现 ANR;所以,从各方面来说,Android 消息机制是为了解决在子线程中无法访问 UI 的矛盾

    上边分析了那么多, 看了那么一大堆的代码,我们清楚的了解到了 Handler 机制对于 Android 系统的重要性;所以也有人说 Handler 消息机制是 Framework 层的发动机,这么考虑一下一点也不为过吧;有需要了解更多关于Android Framework 消息机制 相关资讯的朋友;可以简信发送 "进阶" ,即可获取一份 Android Framework 思维导图及学习手册,以便大家能够更好的学习 Android Framework

    Android Framework 思维导图

    资料部分内容展示如下:

    《Handler 机制之 Thread》

    • 线程概念
    • Android 线程的实现
    • 线程的阻塞
    • 关于线程上下文切换
    • 关于线程的安全问题
    • 守护线程
    • 线程的内存

    《Handler 机制之 ThreadLocal》

    • Java 中的 ThreadLocal
    • ThreadLocal 的前世今生
    • Android 中的 ThreadLocal
    • Android 面试中的关于 ThreadLocal 的问题
    • ThreadLocal 的结构
    • ThreadLocal 静态类 ThreadLocal.Values
    • ThreadLocal 的总结

    完整版 Android Framework 思维导图及学习手册 获取方式: 简信发送 "进阶" 即可 直达获取

    对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们

    技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

    Android 架构师之路还很漫长,与君共勉

    相关文章

      网友评论

        本文标题:Android Framework | Handler 消息机制

        本文链接:https://www.haomeiwen.com/subject/xpgjortx.html