Handler 消息处理机制总结

news2024/9/22 1:12:13

7839d8ee6d0a13db148d5989043a5157.gif

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

一、Handler 简介
二、Handler 消息处理机制原理
三、Handler 机制处理的4个关键对象
四、 Handler常用方法
五、子线程更新UI 异常处理
六、主线程给子线程发送消息的方法
七、主线程发送消息给子线程的例子
八、子线程给主线程发送消息的方法
九、 主、子 线程 互发消息方法
十、子线程方法中调用主线程更新UI的方法
十一、移除Handler 发送的消息方法

一、Handler 简介

在了解Handler 之前,我们需要先了解Handler的继承关系 继承关系如下:

java.lang.Object
   ↳    android.os.Handler

Handler是 Android中用来更新UI 的一套消息处理机制。Handler 允许线程间发送MessageRunnable对象进行通信。在Android中UI修改只能通过UI Thread,子线程不能更新UI。如果子线程想更新UI,需要通过 Handler发送消息给主线程,进而达到更新UI的目的。

二、Handler 消息处理机制原理

Android 应用程序创建的时候,系统会给每一个进程提供一个LooperLooper 是一个死循环,它内部维护一个消息队列,Looper不停的从消息队列中取Message,取到的消息就发送给handler,最后Handler 根据接收的消息去修改UI等。

三、Handler 机制处理的4个关键对象

1.Message

线程之间传递的消息,可以携带一些简单的数据供子线程与主线程进行交换数据。

2.Message Queue

存放通过Handler 发送的Message 的消息队列,每一个线程只有一个消息队列。

3.Handler

消息处理者,主要用于发送跟处理消息。

主要功能:
发送消息SendMessage()
处理消息 HandleMessage()

4.Looper

内部包含一个死循环的MessageQueue,用于存储handler发送的MessageLooper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。

5.总结:

Handler负责发送MessageMessage QueueLooper负责从Message Queue 遍历Message ,然后直接把遍历的消息回传给Handler自己,通过Handler自身的handleMessage处理更新UI等操作。

10316c99ab417250d9c01655703a9173.jpeg

主线程、子线程间通信简单流程

四、 Handler常用方法

1.Runnable对象

  • post(Runnable)

使用方法举例:

public void BtnRunnableMethod(View view) {
        // 1.Runnable 对象
        RunnableHandlderMethod();
    }

    /**
     * Runnable 对象更新 UI 
     * **/
    private Handler mRunnableHandler = new Handler();
    public void RunnableHandlderMethod() {
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);

                    mRunnableHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_runnable))
                                    .setText("Runnable");
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();
    }
  • postAtTime(Runnable, long)

  • postDelayed(Runnable, long)

2. Message 对象

  • sendEmptyMessage(int)

使用方法举例:

public void BtnMessageThreadMethod(View view) {
        // 2.Message 对象
        new MessageHandlerThreadMethod("子线程不能更新UI").start();
    }
    /**
     * Message 对象举例
     * ***/
    private int mCount = 0;
    private Handler mMessageHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            ((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
        }
    };

    class MessageHandlerThreadMethod extends Thread {

        String mString;

        public MessageHandlerThreadMethod(String str) {
            mString = str;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {

                }

                mCount++;
                mMessageHandler.sendEmptyMessage(0);
            }

        }
    }
  • sendMessage(Message)

使用方法举例:

public void BtnMessageObjMethod(View view) {
        HandlerMessageObjMethods();
    }

    /***
     * handler sendmessage 处理方法
     * **/
    private Handler mHandlerMessageObj = new Handler() {

        @Override
        public void handleMessage(Message msg) {

            ((Button) findViewById(R.id.btn_message)).setText("arg1:"
                    + msg.arg1 + "\n" + msg.obj);
        }
    };

    private void HandlerMessageObjMethods() {
        new Thread() {
            @Override
            public void run() {

                try {
                    Thread.sleep(1000);
                    // Message message = new Message();
                    Message message = mHandlerMessageObj.obtainMessage();

                    message.arg1 = 100;

                    Person person = new Person();
                    person.name = "Lucy";
                    person.age = 12;

                    message.obj = person;
                    mHandlerMessageObj.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    class Person {

        public int age;
        public String name;

        public String toString() {
            return "Name=" + name + "\n Age=" + age;
        }

    }
  • sendMessageAtTime(Message, long),

  • sendMessageDelayed(Message, long)

3.接收、处理Message

  • handleMessage(Message)

使用方法举例:

private Handler mMessageHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            ((Button) findViewById(R.id.btn_thread)).setText("" + mCount);
        }
    };

五、子线程更新UI 异常处理

子线程不能更新UI,如果在子线程中更新UI,会出现CalledFromWrongThreadException 异常。

  • CalledFromWrongThreadException

d71b5bbd9361bcf8bb47f05411c28eba.jpeg

CalledFromWrongThreadException 子线程不能更新UI

解决方法:

子线程通过Handler发送消息给主线程,让主线程处理消息,进而更新UI

六、主线程给子线程发送消息的方法

此例子中子线程通过Looper不断遍历主线程发送的消息,Looper使用方法如下:

1. 准备Looper 轮询器

Looper.prepare();

2. Handler 处理遍历消息

Handler mHandler = new Handler()

3. 遍历消息队列

Looper.loop();

4.Looper 使用方法如下:

// 自定义 Loop 线程 ---> 不停的处理主线程发的消息
    class ChildLooperThread extends Thread {
        @Override
        public void run() {
            // 1.准备成为loop线程
            Looper.prepare();
            // 2.处理消息
            mMainHandler = new Handler() {
                // 处理消息
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                            ... ...
                        }
                    });
                }
            };
            // 3.Loop循环方法
            Looper.loop();
        }

    }

七、主线程发送消息给子线程的例子

1. 启动 子线程,并再启动后发送消息

public void BtnMainMessageMethod(View view) {
        // 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
        Message msg = new Message();
        msg.obj = "主线程:这是我携带的信息";
        if (mMainHandler != null) {
            // 2.主线程发送消息
            mMainHandler.sendMessage(msg);
        } else {
            Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息",
                    Toast.LENGTH_SHORT).show();
            // 1.开启轮询线程,不断等待接收主线成消息
            new ChildLooperThread().start();
        }
    }

2. 子线程启动,不停的变量主线程发送的消息

private Handler mMainHandler;
    String mMainMessage;

    // 自定义 Loop 线程 ---> 不停的处理主线程发的消息
    class ChildLooperThread extends Thread {
        @Override
        public void run() {
            // 1.准备成为loop线程
            Looper.prepare();
            // 2.处理消息
            mMainHandler = new Handler() {
                // 处理消息
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    mMainMessage = (String) msg.obj;
                    Log.i("TAG", "子线程:从主线程中接受的消息为:\n" + mMainMessage);
                    // 使用 runOnUiThread 在主线程中更新UI
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_main_message))
                                    .setText(mMainMessage);
                        }
                    });
                }
            };
            // 3.Loop循环方法
            Looper.loop();
        }

    }

八、子线程给主线程发送消息的方法

1.子线程发送消息给主线程方法

public void BtnChildMessageMethod(View view) {

        new Thread() {
            public void run() {
                while (mCount < 100) {
                    mCount++;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    /**
                     * 利用handler 对象发送消息 Message msg=Message.obtain(); Message
                     * msg=new Message(); 获取一个消息对象message
                     * */
                    Message msg = Message.obtain();
                    // 消息标记
                    msg.what = 1;
                    // 传递整型值msg.obj="传递object数据"
                    msg.arg1 = mCount;
                    Log.i("TAG", "count 值=" + mCount);
                    if (mhandler != null) {
                        mhandler.sendMessage(msg);
                    }
                }
            }
        }.start();
    }

2.主线程接收并处理消息的方法

// 定义一个handler 主线程 接收子线程发来的信息
    private Handler mhandler = new Handler() {
        // 處理消息的方法
        public void handleMessage(android.os.Message msg) {

            switch (msg.what) {
            case 1:
                int value = msg.arg1;
                Log.i("TAG", "value值=" + value);
                ((Button) findViewById(R.id.btn_child_message)).setText("当前值="
                        + value);
                break;

            default:
                break;
            }
        }

    };

九、 主、子 线程 互发消息方法

主要实现主、子线程每隔1s中通信一次

  • 实现打印Log如下:

12dc3dab8b943ec1532543a0d7b3cf29.jpeg

主、子线程通信log信息

  • 实现方法如下:

1. 启动子线程并发送给主线程消息

public void BtnMainChildMessageMethod(View view) {

        // 创建 名称为currentThread 子线程
        HandlerThread mChildThread = new HandlerThread("ChildThread");
        mChildThread.start();
        mChildHandler = new Handler(mChildThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {

                Log.i("TAG", "主线程对我说:" + msg.obj);

                // 子线程携带的消息
                Message message = new Message();
                message.obj = Thread.currentThread() + "我是子线程,小样,让我听你的没门";
                // 向主线程发送消息
                mainhandler.sendMessageDelayed(message, 1000);
            }
        };
        // 主线成发送空消息,开启通信
        mainhandler.sendEmptyMessage(1);
    }

2.主线程接收并处理子线程发送的消息

// 创建主线程
    private Handler mainhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.i("TAG", "子线程对我说:" + msg.obj);

            // 主线成携带的消息内容
            Message message = new Message();
            message.obj = Thread.currentThread() + "我是主线程:小子你得听我的。";

            // 向子线程发送消息
            mChildHandler.sendMessageDelayed(message, 1000);
        }
    };

十、子线程方法中调用主线程更新UI的方法

1.Activity 中 可以使用 runOnUiThread(Runnable)

// 使用 runOnUiThread 在主线程中更新UI
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_main_message))
                                    .setText(mMainMessage);
                        }
                    });

2.子线程使用 Handler.post(Runnable)

mRunnableHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ((Button) findViewById(R.id.btn_runnable))
                                    .setText("Runnable");
                        }
                    });

3.View.post()

((Button) findViewById(R.id.btn_runnable)).post(new Runnable() {
                                
                                @Override
                                public void run() {
                                    // TODO Auto-generated method stub
                                    ((Button) findViewById(R.id.btn_runnable)).setText("View.post()方法使用");
                                }
                            });

4.Handler.sendMessage(Message)

public void BtnMainMessageMethod(View view) {
        // 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
        Message msg = new Message();
        msg.obj = "主线程:这是我携带的信息";
        if (mMainHandler != null) {
            // 2.主线程发送消息
            mMainHandler.sendMessage(msg);
            }
}

十一、移除Handler 发送的消息方法

1.移除 handler 发送的所有消息

private Handler mChildHandler;
mChildHandler.removeCallbacksAndMessages(null);

2.移除 指定消息

private Handler mainhandler;
mainhandler.removeMessages(what);

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除。同时感谢您的阅读,期待您的关注。

e198e05444edc985d568719fca662505.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1.Java基础概念-注释和关键字

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 Facts speak louder than words&#xff01; 注释是在程序的指定位置…

Shell脚本发送邮件的详细步骤与配置方法?

Shell脚本发送邮件的进阶技巧&#xff1f;怎么配置Shell脚本发信&#xff1f; 使用Shell脚本发送邮件是一种高效的自动化手段&#xff0c;特别是在需要定期发送报告、通知或警告信息时。AokSend将详细介绍Shell脚本发送邮件的步骤与配置方法&#xff0c;帮助您更好地掌握这一技…

java: 程序包org.springframework.boot.autoconfigure不存在

通过 mvn -U idea:idea 命令重新加载maven包&#xff0c;具体操作是这样的&#xff1a; 打开cmd窗口cd 到 工程根目录&#xff0c;比如我的工程是&#xff1a;D:\IdeaProjects\demo&#xff0c; 执行 mvn -U idea:idea 命令&#xff0c;完了以后重新运行项目就正常了&#xff…

2024年哪家蓝牙耳机性价比高?分享四款高性价比精品耳机产品

随着科技的发展&#xff0c;蓝牙耳机在市场上非常火爆&#xff0c;但很多人不知道该如何选一款适合自己的蓝牙耳机&#xff0c;经常被商家所迷惑&#xff0c;所以常入手到了导致品质低劣甚至买了与价格不匹配的耳机&#xff0c;所以说2024年哪家蓝牙耳机性价比高&#xff1f;作…

可调恒流电子负载如何实现的

可调恒流电子负载是模拟真实负载的电子设备&#xff0c;它可以在电源电压和电流范围内提供恒定的电流或电压。其工作原理主要基于欧姆定律和功率守恒定律。欧姆定律指出&#xff0c;电流通过一个电阻体时&#xff0c;电流的大小与电压的大小成正比&#xff0c;与电阻的大小成反…

跨站请求伪造漏洞

1. 跨站请求伪造漏洞 跨站请求伪造Cross-siterequest forgery (也称为CSRF、XSRF)是一种Web安全漏洞&#xff0c;允许攻击者诱导用户执行他们不打算执行的操作。攻击者通过伪造用户的浏览器的请求&#xff0c;向用户自己曾经认证访问过的网站发送出去&#xff0c;使目标网站接收…

【全网最全】sentinel接入gateway网关,使用nacos做sentinel规则的持久化

文章目录 目标官网接入准备环境配置测试环境启动 规则nacos持久化api分组初始化初始化api分组方式 遗留问题 &#xff1f;初始化api分组,会导致服务无法注册到sentinel dashboard 目标 sentinel接入gateway网关&#xff0c;使用nacos做sentinel规则的持久化 官网 https://se…

经典算法题总结:数组常用技巧(双指针,二分查找和位运算)篇

双指针 在处理数组和链表相关问题时&#xff0c;双指针技巧是经常用到的&#xff0c;双指针技巧主要分为两类&#xff1a;左右指针和快慢指针。所谓左右指针&#xff0c;就是两个指针相向而行或者相背而行&#xff1b;而所谓快慢指针&#xff0c;就是两个指针同向而行&#xf…

夏季养宠难题,浮毛异味难消?别担心,一篇文章帮你解决烦恼

新手铲屎官们最近是不是饱受猫咪浮毛异味困扰&#xff1f;正值夏季&#xff0c;一些猫咪还处于换毛季&#xff0c;疯狂的掉毛加难耐的高温&#xff0c;稍不注意清理&#xff0c;家里就被浮毛异味双重攻击。别担心&#xff0c;那是因为你们少了宠物空气净化器这个好帮手。作为宠…

【机器学习】混淆矩阵(Confusion Matrix)

一、混淆矩阵 True Negative (TN)&#xff1a; 真负类&#xff0c;样本的真实类别是负类&#xff0c;并且模型将其识别为负类&#xff0c;cm[0][0]。False Positive (FP)&#xff1a; 假正类&#xff0c;样本的真实类别是负类&#xff0c;但是模型将其识别为正类&#xff0c;cm…

见合八方发布国产1060nm保偏SOA

天津见合八方光电科技有限公司(以下简称“见合八方”)日前发布新款1060nm保偏半导体光放大SOA蝶形器件。该半导体光放大器SOA具有高饱和输出功率、宽光谱、保偏等特性。主要用于星载空间光通信、空间激光探测、OCT医疗成像等领域。 产品介绍 我司1060nm蝶形器件&#xff0c;具有…

期货的种类之什么是股指期货的类型?

期货可以分为商品期权和股指期货&#xff0c;商品期货的标的物主要是现货&#xff0c;而股指期货的标的物主要是指数。简单来说&#xff0c;就是一种基于股票指数未来的价格变动来进行交易的金融衍生品。它的全称是“股票价格指数期货”&#xff0c;英文简称SPIF。 股指期货怎…

P3957 [NOIP2017 普及组] 跳房子(青春版代码)

[NOIP2017 普及组] 跳房子 - 洛谷 核心思路 单调队列优化dp 顺序 先让合法答案入队 再删去越界答案 判断非空 后 求 答案 一个答案合法 当且仅当 l < dis < r 记 调了n久&#xff0c;找题解调。 竟发现几乎没有用 STL deque 的。 故写了个青春版题解。 AC 代码…

思科RIP动态路由配置3

#路由协议实现# #任务三RIP动态路由配置3# 路由信息协议&#xff08;Routing Information Protocol&#xff0c;RIP&#xff09;是应用较早、使用较普遍的动态路由协议&#xff0c;也是内部网关协议&#xff0c;由于RIP以跳数作为衡量路径的开销&#xff0c;且规定最大跳数为…

zip压缩包的格式不标准导致C++开源unzip.cpp解压失败问题的排查

目录 1、问题描述 2、初步排查 3、查看错误码512对应的含义 4、直接将解压zip包的函数拷贝过来,并将无法解压的zip取来,直接编写测试代码去调试解压过程,最终定位问题 4.1、调试开源unzip.cpp源码的准备工作 4.2、刚解压zip包中最顶层的文件夹就失败了 4.3、是不是zi…

Java如何实现拼音排序?

在我们国家&#xff0c;按拼音进行排序是很常见的需求&#xff0c;比如姓名&#xff0c;那如何按拼音进行排序呢&#xff1f; 假如我们有以下List&#xff1a; List<String> list new ArrayList<>(); list.add("周瑜"); list.add("大都督")…

Redis中缓存穿透、缓存击穿、缓存雪崩的详解

如何理解Redis缓存的穿透、击穿、雪崩问题&#xff1a; 缓存穿透 是指缓存中和数据库中都没有数据&#xff0c;而用户不断访问&#xff0c;导致这个不存在的数据每次请求都要到存储层去查询&#xff0c;这样失去了意义。 缓存穿透的解决方案有哪些? 缓存null值布隆过滤增强…

3D靓图!CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测

3D靓图&#xff01;CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测 目录 3D靓图&#xff01;CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Ma…

【问题解决】git status中文文件名乱码

问题复现 解决办法 在git bash中直接执行如下命令 git config --global core.quotepath false原因 通过 git config --help 可以查看到以下内容&#xff1a; core.quotePath Commands that output paths (e.g. ls-files, diff), will quote “unusual” characters in the p…

vue3 更改 element-plus el-collapse 折叠面板样式

官网提供 展示效果 样式 更改箭头指向 :deep(.el-collapse-item__arrow){ transform: rotate(-270deg); } :deep(.el-collapse-item__header.is-active .el-collapse-item__arrow) { transform: rotate(270deg); } 二 &#xff1a; 或者隐藏右侧箭头 :deep(.el-collapse-i…