Handler机制小结

简介: Handler笔记什么是handler机制?handler机制的主要成员1、handler:负责发送处理消息2、message:消息的信息的载体3、messageQueue:存放message的队列4、looper:handler机制的动力...

Handler笔记

什么是handler机制?
handler机制的主要成员
1、handler:负责发送处理消息
2、message:消息的信息的载体
3、messageQueue:存放message的队列
4、looper:handler机制的动力,无限循环的从messageQueue队列中取出message给handler

img_ca281cf242ba504d9c49bea0847ec784.jpe
图1

1、Handler

(1)我们先来看看handler的构造方法

      public Handler() {
        this(null, false);
    }
    
     public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        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;
    }

(2)默认的构造方法走了 Handler(Callback callback, boolean async) 构造方法,此方法用 mLooper = Looper.myLooper();获取了looper,然后又用looper创建了messageQueue

2、Looper

在Looper类中

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }  

注意上面的注释 返回一个与当前线程关联的Looper对象
那Looper是怎么创建的呢,既然有get方法,那应该就有set方法。搜索sThreadLocal.set,果然找到了

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

说明prepare方法直接创建了一个当前线程的Looper

在UI主线程中,会默认的创建好Looper对象,而在子线程中要使用本线程的Handler需要手动创建Looper对象,如下

class LooperThread extends Thread {
        public Handler mHandler;

        public void run() {
            Looper.prepare();

            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    
                }
            };

            Looper.loop();
        }
    }   

3、MessageQueue

在handler中我们知道MessageQueue对象通过
mQueue = mLooper.mQueue;
其中的消息是怎么来的呢我们我们通过handler的sendMessage()方法来进行分析

public final boolean sendMessage(Message msg)
   {
       return sendMessageDelayed(msg, 0);
   }  

handler的sendMessage()方法又调用了

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

sendMessageAtTime()又调用了

 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);
    }

此方法先获取了全局的messageQueue对象mQueue最后通过MessageQueue.enqueueMessage()将message存到了messageQueue队列中

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
通过上面的分析我们知道了messageQueue是怎么进数据的,后边我们分析一下怎么从里面取数据

我们在子线程中使用Handler的示例中最后一步 调用Looper.loop();然后我们来看看

    public static void loop() {
        final Looper me = myLooper();
        ······
        for (;;) {
            Message msg = queue.next(); // might block
            ······
             try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ······
        }
    }

在Looper.loop();方法中我们可以看到for循环中从queue中取出message,然后message是怎么给到handler呢?
在msg.target.dispatchMessage(msg);这一步中我们先看看target是个什么?转到message类中会发现

/*package*/ Handler target;

target是个handler实例 由此可以得出整个message的传播回路

总结:

在一个线程中创建handler、looper、messageQueue
handler把带有信息的message通过sendmessage()发送给messageQueue队列,然后looper通过Looper.loop()无限循环从messageQueue中取出message,然后在发送给handler,handler在handleMessage()方法中处理信息

最后感谢前人的好文章的指导,个人根据个人的理解总结如有不对的地方,请留言。
https://blog.csdn.net/reakingf/article/details/52054598
https://blog.csdn.net/pgg_cold/article/details/79400435?utm_source=blogxgwz2
https://blog.csdn.net/qian520ao/article/details/78262289?locationNum=2&fps=1
https://blog.csdn.net/qq_32770809/article/details/79132363?utm_source=blogxgwz1

相关文章
|
3月前
|
前端开发
官网导航更智能:CSS动画下划线,让每一次点击都充满期待!
官网导航更智能:CSS动画下划线,让每一次点击都充满期待!
|
6月前
|
Java
LinkedBlockingDeque的源码解析(基于JDK1.8)
LinkedBlockingDeque的源码解析(基于JDK1.8) LinkedBlockingDeque是Java中的一个阻塞双端队列,它继承自AbstractQueue类并实现了BlockingDeque接口。在多线程环境下,LinkedBlockingDeque能够提供高效的并发访问能力。下面我们来看一下它的源码实现。
|
SQL 消息中间件 分布式计算
《Apache Flink 案例集(2022版)》——5.数字化转型——移动云Apache Flink 在移动云实时计算的实践(上)
《Apache Flink 案例集(2022版)》——5.数字化转型——移动云Apache Flink 在移动云实时计算的实践(上)
267 0
|
Go iOS开发
iOS应用发布ITMS-90704错误解决
iOS应用发布ITMS-90704错误解决 今天第一次用XCode 9 GM版打包上传应用。貌似上传的过程更简单了。选择 “Automatically manage signing” (自动管理签名)后它就直接显示一个汇总信息列表,然后开始上传了,比以前简化了一两个步骤。
|
分布式计算 容灾 大数据
MaxCompute( 原名ODPS)大数据容灾方案与实现(及项目落地实例)专有云
一,背景与概述    复杂系统的灾难恢复是个难题,具有海量数据及复杂业务场景的大数据容灾是个大难题。    MaxCompute是集团内重要数据平台,是自主研发的大数据解决方案,其规模和稳定性在业界都是领先的。
6405 0
☆打卡算法☆LeetCode 100、相同的树 算法解析
“给定两颗二叉树的根节点,编写函数来检验这两棵树是否相同。”
|
关系型数据库 MySQL Linux
阿里云轻量应用服务器教程–如何链接网站数据库(镜像:WordPress)
轻量应用服务器提供多种应用环境,不同的环境有使用不同的数据库,链接数据库的方式也各部相同。我们这里以轻量Wordpress镜像为例说明。 获取Mysql root密码 在轻量控制台-->应用管理-->应用详情-->Mysql信息内点击远程链接,然后复制查看命令 执行复制的命令,查看数据库root密码 远程进入服务器 本镜像默认只允许本机访问Mysql数据库,故我们需要远程登陆到轻量应用服务器上,然后操作Mysql数据库。
6911 0
|
大数据 分布式数据库 Hbase
大数据_学习_01_Hadoop 2.x及hbase常用端口及查看方法
    二、参考资料 1.Hadoop 2.x常用端口及查看方法
1234 0
|
Linux Go Windows
linux中pci设备知识
linux中pci设备知识   Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分。PCI(Periheral Component Interconnect)有三种地址空间:PCI I/O空间、PCI内存地址空间和PCI配置空间。
5446 0