Android-Handler详解_使用篇

本文我将从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详解_原理解析》两篇.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

国际伦敦金行情分析中的趋势分析方法

国际伦敦金行情走势复杂多变。近期&#xff0c;金价曾经一度刷新历史的新高点至2222&#xff0c;但就在当天&#xff0c;金价又快速下跌跌超过30美元。不过这么多变的伦敦金行情也为我们的交易创造了空间&#xff0c;有空间就等于有机会&#xff0c;只要我们能够掌握国际伦敦金…

AWS SES发送邮件时常见的错误及解决方法?

AWS SES发送邮件如何做配置&#xff1f;使用AWS SES发信的限制&#xff1f; 在使用AWS SES发送邮件时&#xff0c;可能会遇到一些常见的错误。AokSend将介绍一些常见的AWS SES发送邮件错误及其相应的解决方法&#xff0c;帮助用户更好地利用AWS SES进行邮件发送。 AWS SES发送…

在 Windows 11 上安装 MongoDB

MongoDB 是一个流行的 NoSQL 数据库&#xff0c;它提供了灵活的数据存储方案&#xff0c;而 MongoDB Compass 则是一个可视化管理工具&#xff0c;可以更轻松地与 MongoDB 数据库交互和管理。在本文中&#xff0c;我们将介绍如何在 Windows 11 上安装 MongoDB&#xff0c;并配置…

手机短信验证码自动转发到服务器

今天写一个自动化处理程序&#xff0c;需要验证码登录&#xff0c;怎么样把手机收到的短信自动转发到服务器接口呢&#xff1f; 利用ios手机快捷指令的功能 打开快捷指令点击中间自动化点击右上角号选择信息信息包含选取&#xff0c;输入验证码选择立即执行点击下一步按下图配…

JavaWeb解压缩漏洞之ZipSlip与Zip炸弹

前言 前面一篇博文《Android Zip解压缩目录穿越导致文件覆盖漏洞》介绍过 Android 系统 Zip 文件解压缩场景下的目录穿越漏洞&#xff0c;近期在学习 JavaWeb 代码审计的时候从 github 看到《OpenHarmony-Java-secure-coding-guide.md》中“从 ZipInputStream 中解压文件必须进…

搭建机器人产业发展重要展示平台“2024南京国际机器人展览会”

2024南京国际智能机器人展览会 2024 Nanjing Intelligent Robot Expo 时间:2024年11月22-24日 地点:南京国际博览中心 南京&#xff0c;这座历史悠久的文化名城&#xff0c;如今正站在机器人产业发展的前沿。随着全球科技的飞速进步&#xff0c;机器人产业已经成为推动经济社…

记一次由gzip引起的nginx转发事故

故事背景 书接前几篇文章&#xff0c;仍然是交付甲方遇到的一个特殊诉求&#xff0c;从而引发了本期的事故。甲方的诉求是前端的请求过来&#xff0c;需要加密&#xff0c;但是要经过waf&#xff0c;必须要求是请求明文&#xff0c;那就要在waf和nginx之间做一个解密前置应用处…

网络链路层之(2)PPP协议

网络链路层之(2)PPP协议 Author: Once Day Date: 2024年3月27日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 通信网络技术_Once-Day的博客-CSDN…

pt-archiver的实践分享,及为何要用 ob-archiver 归档数据的探讨

作者简介&#xff1a;肖杨&#xff0c;软件开发工程师 在数据密集型业务场景中&#xff0c;数据管理策略是否有效至关重要&#xff0c;它直接关系到系统性能与存储效率的提升。数据归档作为该策略的关键环节&#xff0c;不仅有助于优化数据库性能&#xff0c;还能有效降低存储成…

(一)基于IDEA的JAVA基础9

循环结构及特点 while循环 do while循环 for 循环 特点:有循环条件 循环起始值 循环自增量(每次自增的量) 循环操作 while循环 语法: 初始部分//定义变量的地方&#xff0c;声明变量 while(循环条件){ 循环操作&#xff1b; 迭代部分&#xff1b; } 流程图: 练习:打…

Rust编程(三)生命周期与异常处理

生命周期 生命周期&#xff0c;简而言之就是引用的有效作用域。在大多数时候&#xff0c;我们无需手动的声明生命周期&#xff0c;因为编译器可以自动进行推导。生命周期的主要作用是避免悬垂引用&#xff0c;它会导致程序引用了本不该引用的数据&#xff1a; {let r;{let x …

【办公类-21-11】 20240327三级育婴师 多个二级文件夹的docx合并成docx有页码,转PDF

背景展示&#xff1a;有页码的操作题 背景需求&#xff1a; 实操课终于全部结束了&#xff0c;把考试内容&#xff08;docx&#xff09;都写好了 【办公类-21-10】三级育婴师 视频转文字docx&#xff08;等线小五单倍行距&#xff09;&#xff0c;批量改成“宋体小四、1.5倍行…

2024 MCM数学建模美赛2024年A题复盘,思路与经验分享:资源可用性与性别比例 | 性别比例变化是否对生态系统中的其他生物如寄生虫提供优势(五)

审题 第四问让我们探究性别比例变化是否对生态系统中的其他生物如寄生虫提供优势。这里我们可以把问题简化一下&#xff0c;只探究性别比例会不会对寄生虫提供优势。因为考虑太多生物&#xff0c;会使模型更复杂&#xff0c;我这个水平处理不了这么复杂的问题&#xff0c;是我…

整数在内存里面的存储

整数在内存里面的存储 整数在计算机里面的存储是按照二进制的方式进行存储 显示的时候是按照16进制的方法进行显示 1. 整数在内存中的存储在讲解操作符的时候&#xff0c;我们就讲过了下⾯的内容&#xff1a;整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码 三种…

案例研究|DataEase实现物业数据可视化管理与决策支持

河北隆泰物业服务有限责任公司&#xff08;以下简称为“隆泰物业”&#xff09;创建于2002年&#xff0c;总部设在河北省高碑店市&#xff0c;具有国家一级物业管理企业资质&#xff0c;通过了质量体系、环境管理体系、职业健康安全管理体系等认证。自2016年至今&#xff0c;隆…

FIM配置

FIM&#xff08;功能抑制管理器&#xff09; FIM模块根据DTC状态来确定对应功能是否要禁止 FiM_GetFunctionPermission通过RTE提供给SWC FiMFIDs FiMInhibitionConfigurations FiMInhFunctionIdRef&#xff1a;关联FIMID FiMInhInhibitionMask: FIM_LAST_FAILED Inh Event…

【氮化镓】p-GaN栅极退化的温度和结构相关性

论文总结&#xff1a; 本文献深入研究了带有p-GaN栅极的正常关断型(normally-off)高电子迁移率晶体管(GaN-HEMTs)在恒定电压应力下的时序退化行为。通过直流特性分析和温度依赖性分析&#xff0c;研究了故障时间(TTF)与应力温度和器件几何结构的依赖性。结果显示&#xff0c;p…

算法打卡day19

今日任务&#xff1a; 1&#xff09;235. 二叉搜索树的最近公共祖先 2&#xff09;701.二叉搜索树中的插入操作 3&#xff09;450.删除二叉搜索树中的节点 235. 二叉搜索树的最近公共祖先 题目链接&#xff1a;235. 二叉搜索树的最近公共祖先 - 力扣&#xff08;LeetCode&…

Android 自定义EditText

文章目录 Android 自定义EditText概述源码可清空内容的EditText可显示密码的EditText 使用源码下载 Android 自定义EditText 概述 定义一款可清空内容的 ClearEditText 和可显示密码的 PasswordEditText&#xff0c;支持修改提示图标和大小、背景图片等。 源码 基类&#xf…

大语言模型(LLM)token解读

1. 什么是token&#xff1f; 人们经常在谈论大模型时候&#xff0c;经常会谈到模型很大&#xff0c;我们也常常会看到一种说法&#xff1a; 参数会让我们了解神经网络的结构有多复杂&#xff0c;而token的大小会让我们知道有多少数据用于训练参数。 什么是token&#xff1f;比…