Android-Handler详解_使用篇

news2024/9/30 5:24:34

本文我将从Handler是什么、有什么、怎们用、啥原理,四个方面去分析。才疏学浅,如有错误,欢迎指正,多谢。 

1.是什么

因为Android系统不允许在子线程访问UI组件,否则就会抛出异常。所以咱们平实用的最多的可能是在子线程将更新UI的任务传达给UI线程去执行。但是别误会,它绝不仅仅是用来在子线程更新UI。Handler可以将任何线程的任务切换到它所在的线程去执行。

总体的流程其实就是: 初始化Handler并初始化CallBack重写handleMessage方法用来接收消息。handler的sendMessage发送消息。嗯嗯看起来好像就这么简单,我们实际用一下。

2.有什么

Handler的运行机制要依赖其他三个重要的类,分别是Message、MessageQueue、Looper。

2.1 Handler

最重要的作用就是,负责发消息和收消息。

2.2 Message

Message一看便知,是消息,消息机制肯定离不开消息这个载体啊,Messege就是消息本息。

它包含几个比较重要的成员变量what、arg1、arg2、都是int类型,what我们一般用来区分不同的消息;obj是个Object可以传递对象,一般传递数据主要在这个里面;还有target,它是Handler类型的,我们能接收到消息target功不可没《划重点》。

创建Message对象可以直接new Message() 也可以通过Message.obtain()获取,建议使用后者。然后通过handler的sendMessage(message)就把消息发出去了。上面提到的几个变量我们可以给他们赋值也可以不赋值,但是建议最少给what赋个值,如果都不赋值收到多个消息时会无法区分甲乙丙丁谁是谁,仅一条消息时您随意。发送消息还有sendEmptyMessage空消息不用message、sendMessageDelayed延时消息、sendEmptyMessageDelayed延时空消息、sendEmptyMessageAtTime等。

2.3 MessageQueue

MessageQueue消息队列专门用来存储消息,起名稍微有点误导性,它数据结构不是一个队列而是一个单列表。

此刻先记住他有两个重要的方法enqueueMessage和next(),前者用来存储消息后面用来从中取出消息并发送给handler的handlerMessage,注意这里的next是MessageQueue中方法和上面提到的Message类的next变量别混淆。
MessageQueue不生产消息,是个仓库管理员,只负责入库和出库。

看看它为啥是个单链表吧

链表就是对象的循环调用。当前要处理的messge是放到MessageQueue的mMessages变量,MessageQueue的mMessages变量是个Message,而Message类有一个next变量也是Message类型,同时这个Message类型的next变量又有自己的next变量,层层嵌套。后面说原理时具体分析。先上图,内容太多没有截取完整但能表达清楚关系:这里是使用下面3.1中方式一发送消息的断点截图。

2.4 Looper

Looper 负责取消息并发给handler的handlerMessage方法,核心方法是loop()它会调用MessageQueue的next()方法取消息并调用Handler的dispatchMessage(msg)将消息发送给handleMessage回到方法 ;

因为使用Handler必须要有looper因此它还提供了prepare()方法可以帮你实例化一个looper并存在当前线程的ThreadLocal中,因为你当前线程可能不是在主线程但你又要在主线程接收消息测试可以调用另一个方法getMainLooper,他可以为你提供应用主线程的looper,它的注释是这样写的:Returns the application's main looper, which lives in the main thread of the application.

3.怎么用

这里我根据初始化handler对象的不同区分了五种使用场景,后续也都会以方式一、方式二、方式三、方式四、方式五来区分。

  1. Handler发送Message
    1. 方式一  自定义一个TestHandler类继承Handler,并使用弱引用,避免内存泄漏,否则会一片黄色报警很不美观,里面写了两个构造方法分别是空参的Handle()和Handler(@NonNull Looper looper),实现主线程和子线程都能发消息,其实还有其他的构造方法我们后面分析。
               //插入方式一对应的代码

先定义TestHandler

public static class TestHandler extends Handler{

    private final WeakReference<Activity> weakReference;
    private final HandlerActivity activityWeak;
    //给方式一用
    public TestHandler(HandlerActivity activity){
        super();
        weakReference = new WeakReference<>(activity);
        activityWeak = (HandlerActivity) weakReference.get();
        activityWeak.handlerTv4.setText("已赋值");
    }
    //给方式三用
    public TestHandler(HandlerActivity activity,Looper looper){
        super(looper);
        weakReference = new WeakReference<>(activity);
        activityWeak = (HandlerActivity) weakReference.get();
    }
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        activityWeak.doMessage(msg);
    }
}

//初始化handler

testHandler = new TestHandler(this);

方式一发消息

//                Message message1 = new Message();
                Message message1 = Message.obtain();
                message1.what = WHAT1;
                testHandler.sendMessage(message1);

                Message message2 = Message.obtain();
                message2.what = WHAT2;
                message2.obj = "空消息";
                testHandler.sendMessage(message2);

                Message message3 = Message.obtain();
                message3.what = WHAT3;
                message3.obj = "延时消息";
                testHandler.sendMessageDelayed(message3, DELAYTIME_1600);

                Message message4 = Message.obtain();
                message4.what = WHAT4;
                message4.obj = "延时空消息";
                testHandler.sendMessageDelayed(message4, DELAYTIME_1600);

                Message message5 = Message.obtain();	
                message5.what = WHAT5;
                message5.obj = "延时空消息";
                // 这个也是延时消息,在第二个参数后发出,但他是相较于最近一次开机时间的,因此基本桑拿是秒发,而且还会插队到其他消息前面,改成90000000L就一时半会收不到了。从
                testHandler.sendEmptyMessageAtTime(WHAT5,DELAYTIME_1600);

方式二  

直接传入一个 CallBack,Handler(@Nullable Callback callback),CallBack可以直接new也可以改变成把CallBack单独提出来,效果一样

        testHandler2 = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        doMessage(message);
        return false;
    }
});


方式三

Handler(@NonNull Looper looper),传入一个looper,这样就可以在任何线程创建Handler了,我这里用的主线程的Looper所以在子线程这样写就可以把任务发到主线程去执行了。

为了验证传入Looper真实有效特意在子线程发送的。

                        new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //方式三
                        TestHandler testHandler3 = new TestHandler(HandlerActivity.this, Looper.getMainLooper());
                        Log.e(TAG, "onClick: handlerTv3    ThreadId="+ Thread.currentThread().getId() );
                        Message message1 = Message.obtain();
                        message1.what = WHAT1;
                        testHandler3.sendMessage(message1);
                    }
                }).start();

用主线程创建的handler,用来子线程发消息OK,正常就这么用;
用子线程创建的handler如果没有设置looper就会在收不到消息并且报一个错:This is not main thread, and the caller should invoke Looper.prepare()  and Looper.loop()called byandroid.os.Handler.<init>:122 com.example.testdemo3.activity.HandlerActivity$TestHandler.<init>:170 com.example.testdemo3.activity.HandlerActivity$4$1.run:151 java.lang.Thread.run:929 <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack>
 创建handler实例时传入主线程的Looper.getMainLooper()就能在主线程收到


方式四

传入Looper和Handler.Callback

//有系统版本限制
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    Message message = Message.obtain();
    message.what = 5;
    //方式四
    Handler handler = Handler.createAsync(Looper.myLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            Log.e(TAG, "createAsync-handleMessage:  what= "+msg.what );
            return false;
        }
    });

}


方式五  

它不是一种实例化Handler的方式,方便下文引用它因此请他入列位列末席。一种特殊的发消息方式,是post一个Runnable。它基于以上四种方式任何一个,只是改变了发送方式采用了post但后面实际也会转成sendMessagexx方法(它也有postDelayed等延时方法,使用方法类似请各位大佬自行查看源码),这是在post方法的run回调方法里接收,但没法区分是哪个

testHandler.post(new Runnable() {
    @Override
    public void run() {
        Log.e(TAG, "run:  收到消息但不知道是什么" );
    }
});

小贴士:

提示一:同一个handler多次发送同一条消息

handler.sendMessage(message);
handler.sendMessage(message);    

如果同一个handler发送同一条消息连续发送两遍会引发闪退,下面的报错:
 java.lang.IllegalStateException: { when=0 what=5 target=android.os.Handler } This message is already in use.

提示二:

上面方式三在子线程发消息就是调用了Looper.getMainLooper()这里可能比较好奇,既然使用Handler必须要有一个Looper那主线程的looper是哪来的? 原来在应用刚启动时在ActivityThread的main方法中已经调用了。

prepareMainLooper方法会调用prepare()方法可以帮你实例化一个looper并存在当前线程的ThreadLocal中,并且还有贴心的存在了Looper中提供了getMainLooper供人调用,真相大白了。同样looper.prepare()也会在threadlocal存一个looper后面mylooper()方法会将其取出来使用。
提示三:

一个线程只能有一个Looper,在创建looper对象时如果超过一个就会抛异常Only one Looper may be created per thread,因此不要多次调用prepare方法创建looper对象。

为了方便阅读将文章分为《Android-Handler详解_使用篇》和《Android-Handler详解_原理解析》两篇.

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

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

相关文章

「媒体宣传」如何针对不同行业制定媒体邀约方案

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 针对不同行业制定媒体邀约方案时&#xff0c;需要考虑行业特点、目标受众、媒体偏好以及市场趋势等因素。 一、懂行业 先弄清楚你的行业是啥样&#xff0c;有啥特别之处。 了解行业的热…

【地图构建(1)】占用栅格地图构建Occupancy grid mapping

本文主要参考Probabilistic Robotics《概率机器人》一书。 其他参考&#xff1a; 弗莱堡大学课件 博客 含代码博客 0.引言 位姿已知的地图构建(mapping with known poses)的定义&#xff1a;已知机器人的位姿 x 1 : t x_{1:t} x1:t​和传感器的观测数据 z 1 : t z_{1:t} z1:t…

绝地求生:报告长官!速去领取PUBG7周年礼包及7周年活动攻略【附方法】

奖励都需要长官们绑定全球账号&#xff0c;在游戏个人资料处查看是否有绑定&#xff01; PUBG七周年礼包详情&#xff1a; 包含7周年快乐甜筒帽 7周年快乐背包&#xff08;3级&#xff09; 戴墨镜的幽灵 黑货票券 x30 档案管理员宝箱 x1 钥匙 x1 绑定ID登录&#xff0c;或…

【FIneBI可视化工具的使用】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;书生♡&#xff0c;今天主要和大家分享一下可视化的工具FineBI的详细使用,希望对大家有所帮助。感谢大家关注点赞。 &#x1f49e;&#x1f49e;前路漫漫&#xff0c;希望大家坚持下去&#xff0c;不忘初心&…

大型驱动水冷负载电阻、缓冲器、滤波器和快速放电电阻

EAK业界首创双面水冷负载电阻器&#xff0c;独特的设计&#xff0c;用户更方便的串联并联使用&#xff0c;强大的水流带走更多因充放电带来的热量。AlN高可靠性氮化铝基板保证了热膨胀不会影响电阻的工作。 液冷电阻器使用水或离子水作为冷却剂。通过添加乙二醇&#xff0c;可以…

单调栈(C++)

单调栈,即栈中元素是单调递增的或是单调递减的,是一个比较好用的数据结构. 柱状图中最大的矩形 84. 柱状图中最大的矩形 - 力扣&#xff08;LeetCode&#xff09; 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。…

【SAP2000】在框架结构中应用分布式面板荷载Applying Distributed Panel Loads to Frame Structures

在框架结构中应用分布式面板荷载 Applying Distributed Panel Loads to Frame Structures 使用"Uniform to Frame"选项,可以简单地将荷载用于更多样化的情况。 With the “Uniform to Frame” option, loads can be easily used for a greater diversity of situat…

【书生·浦语大模型实战营第二期】学习笔记1

1. Introduction 开源llm举例&#xff1a;LLaMA 、Qwen 、Mistral 和Deepseek 大型语言模型的发展包括预训练、监督微调&#xff08;SFT&#xff09;和基于人类反馈的强化学习&#xff08;RLHF&#xff09;等主要阶段 InternLM2的显著特点 采用分组查询注意力&#xff08;GQA…

蓝桥杯基础练习汇总详细解析(三)——字母图形、01字符串、闰年判断(详细解题思路、代码实现、Python)

试题 基础练习 字母图形 提交此题 评测记录 资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 利用字母可以组成一些美丽的图形&#xff0c;下面给出了一个例子&#…

web开发发展历程-前端、后端、消息队列、后端架构演进

文章目录 摘要主要内容不同的时代对应不同的技术前端技术的中间阶段-单页面应用前后端分离后端技术演化-云计算平台总体趋势反应式编程 消息队列发展史kafka&#xff0c;rocketmq&#xff0c;pulsar网易后端架构演进架构瓶颈数据库瓶颈服务器瓶颈数据库缓存瓶颈-缓存击穿、雪崩…

Spring Boot:Web开发之三大组件的整合

Spring Boot 前言Spring Boot 整合 ServletSpring Boot 整合 FilterSpring Boot 整合 Listener 前言 在 Web 开发中&#xff0c;Servlet 、Filter 和 Listener 是 Java Web 应用中的三大组件。Servlet 是 Java 代码&#xff0c;通过 Java 的 API 动态的向客户端输出内容。Filt…

7.3*3卷积核生成

1.卷积核 在数字图像处理中的各种边沿检测、滤波、腐蚀膨胀等操作都离不开卷积核的生成。下面介绍如何生成各种3X3的卷积核。为后面的数字图像操作打下基础。   由于图像经过卷积操作后会减少两行两列&#xff0c;因此在生成卷积核的时候一般会对图像进行填充&#xff0c;填充…

day 36 贪心算法 part05● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

一遍过。首先把区间按左端点排序&#xff0c;然后右端点有两种情况。 假设是a区间&#xff0c;b区间。。。这样排列的顺序&#xff0c;那么 假设a[1]>b[0],如果a[1]>b[1]&#xff0c;就应该以b[1]为准&#xff0c;否则以a[1]为准。 class Solution { public:static bo…

一个基于.NET Core构建的简单、跨平台、模块化的商城系统

前言 今天大姚给大家分享一个基于.NET Core构建的简单、跨平台、模块化、完全开源免费&#xff08;MIT License&#xff09;的商城系统&#xff1a;Module Shop。 商城后台管理端功能 商品&#xff1a;分类、品牌、单位、选项&#xff08;销售属性&#xff09;、属性、属性模…

人脸68关键点与K210疲劳检测

目录 人脸68关键点检测 检测闭眼睁眼 双眼关键点检测 计算眼睛的闭合程度&#xff1a; 原理: 设置阈值进行判断 实时监测和更新 拓展&#xff1a;通过判断上下眼皮重合程度去判断是否闭眼 检测嘴巴是否闭合 提取嘴唇上下轮廓的关键点 计算嘴唇上下轮廓关键点之间的距…

LangChain入门:2.OpenAPI调用ChatGPT模型

快速入门 本篇文章正式进入LangChain的编码阶段&#xff0c;今天实现的功能是使用OpenAPI调用ChatGPT模型来进行文本问答。 1. 申请OpenAPI的访问令牌 这里介绍两种获取到OpenAPI访问令牌的方式&#xff0c;大家按照自己需求进行选择&#xff0c;之后的文章我会基于第二种选…

政安晨:【深度学习神经网络基础】(二)—— 神经元与层

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 政安晨的机器学习笔记 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 神经元是深度学习神经网络中的基本单元&#xff0c;模拟了…

淘宝详情数据采集(商品上货,数据分析,属性详情,价格监控),海量数据值得get

淘宝详情数据采集涉及多个环节&#xff0c;包括商品上货、数据分析、属性详情以及价格监控等。在采集这些数据时&#xff0c;尤其是面对海量数据时&#xff0c;需要采取有效的方法和技术来确保数据的准确性和完整性。以下是一些关于淘宝详情数据采集的建议&#xff1a; 请求示…

DevSecOps平台架构系列-互联网企业私有化DevSecOps平台典型架构

目录 一、概述 二、私有化DevSecOps平台建设思路 2.1 采用GitOps公有云建设 2.2 采用GitOps私有云建设 2.3 总结 三、GitOps及其生态组件 3.1 采用GitOps的好处 3.1.1 周边生态系统齐全 3.1.2 便于自动化的实现 3.1.3 开发人员属性GitOps 3.2 GitOps部分生态组件介绍…

红黑树的Java实现

红黑树的Java实现 文章目录 红黑树的Java实现一、概述二、添加元素三、删除元素四、完整代码总结 一、概述 红黑树也是一种二叉平衡搜索树&#xff0c;向比与AVL树&#xff0c;是一种弱平衡树。因为AVL树是通过平衡因子&#xff0c;左右树的高度相差不能大于1来保证平衡&#…