Android的Handler

1. Handler是用于线程间通信,本质上是:

Handler调用发送方法,向与Looper绑定的消息队列写入消息,然后Looper.loop()会循环的从消息队列里拿出消息。并调用dispatchMessage处理消息。而需要此消息的线程会实现回调的handleMessage接口来处理消息。

2.举个例子:主线程调用子线程的Handler发送消息。

package com.android.car.myapplication;import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private TextView textView;private Handler mainHandler;private Handler backgroundHandler;private Thread backgroundThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_carsetting);textView = findViewById(R.id.textView);// 主线程的 Handler,用于更新 UImainHandler = new Handler(Looper.getMainLooper());// 创建子线程并启动它backgroundThread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("childThread prepare");Looper.prepare();// 在子线程中创建一个 Handler// 充当其他线程调用 backgroundHandler.sendMessage/backgroundHandler.post的接收端backgroundHandler = new Handler(Looper.myLooper()) {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {System.out.println("childThread receive Msg 1");}}};System.out.println("childThread Looping");Looper.loop();}});backgroundThread.start();// 等待线程跑起来try {sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}Message message = backgroundHandler.obtainMessage(1);backgroundHandler.sendMessage(message);System.out.println("MainThread: sendMsg 1");}}
  • 日志 

2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread prepare
2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread Looping
2024-11-07 16:25:56.910 12880-12880 System.out       com...myapplication  I  MainThread: sendMsg 1
2024-11-07 16:25:56.912 12880-12902 System.out       com...myapplication  I  childThread receive Msg 1

3. Handler使用的源码解析 

  • 3.0 调用端使用sendMessage来发送消息
Message message = backgroundHandler.obtainMessage(1);
backgroundHandler.sendMessage(message);

追sendMessage的调用栈,最终会调用到Handler.java的enqueueMessage函数

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;   // 
。。。。。return queue.enqueueMessage(msg, uptimeMillis);}

1)msg.target指定了this,即当前的Handler, 对应3.3中取出消息的地方msg.target为handler, 且这个Handler是backgroundHandler。

2)enqueueMessage将msg放入了消息队列。

  • 3.1 Looper.prepare();
    public static void prepare() {prepare(true);}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));}
  • 3.2 Handler backgroundHandler = new Handler(Looper.myLooper())
    public Handler(@NonNull Looper looper) {this(looper, null, false);}@UnsupportedAppUsagepublic Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {this(looper, callback, async, /* shared= */ false);}public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,boolean shared) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;mIsShared = shared;}
  • 3.3 Looper.loop(); 关键是loopOnce()从消息队列中循环拿消息,

假设现在调用了3.0中的sendMessage方法,此时消息队列中已经有了消息。

那么调用me.mQueue.next();即可拿到这条message.

再调用dispatchMessage处理消息。

private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg = me.mQueue.next(); // might block
。。。。msg.target.dispatchMessage(msg);。。。。。msg.recycleUnchecked();
。。。。。return true;}

3.0 讲过了msg.target是backgroundHandler,再往下查看dispatchMessage的实现:

    public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);  // 调用者overide 实现的 handleMessage函数}}

会发现其中的handleMessage是在 2.当中实现的handleMessage方法。

4. nativePollOnce(ptr, nextPollTimeoutMillis);

nativePollOnce, 就是linux中的poll函数,可以让出线程对cpu的占用。

没有消息则线程进入空闲状态,不用一直调用来浪费cpu, 这种等待是非阻塞的。

5.总结:

在子线程中:

  • Looper.prepare()初始化了MessageQueue,与当前线程绑定。
  • Handler通过传入当前的Looper实现与Loop绑定,
  • Handler通过override实现了handleMessage。
  •  再通过Looper.loop()开启死循环来处理消息。

其他线程:

  • 调用子线程的Handler的post/sendMessage方法,来向目标线程的MessageQueue写入消息。

子线程:

  • 子线程调用Loop.loop()拿到消息,并调用dispatchMessage处理消息。
  • 在调用子线程override的handleMessage方法来处理来自其他线程的消息。

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

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

相关文章

Spark的yarn集群环境搭建

一.为什么要搭建yarn集群 为什么要将Spark的程序运行在YARN上,不运行在自带的 Standalone集群上? 1、统一化资源管理 Standalone是Spark专用的资源管理集群,只能用于运行 Spark程序 YARN是功能的分布式资源管理平台,可以运行各种分…

51单片机教程(六)- LED流水灯

1 项目分析 基于点亮LED灯、LED灯闪烁,扩展到构成最简单、花样流水灯。 2 技术准备 1 流水灯硬件及原理图 流水灯是由多个LED灯组成的 2 C语言知识点 数组 数组声明:长度不可变 数据类型 数组名称[长度n] // 整数型默认为0,小数型默认…

PyQt5实战——翻译的实现,第一次爬取微软翻译经验总结(八)

个人博客:苏三有春的博客 系类往期文章: PyQt5实战——多脚本集合包,前言与环境配置(一) PyQt5实战——多脚本集合包,UI以及工程布局(二) PyQt5实战——多脚本集合包,程序…

前端好用的网站分享——CSS(持续更新中)

1.CSS Scan 点击进入CSS Scan CSS盒子阴影大全 2.渐变背景 点击进入color.oulu 3.CSS简化压缩 点击进入toptal 4.CSS可视化 点击进入CSS可视化 这个强推,话不多说,看图! 5.Marko 点击进入Marko 有很多按钮样式 6.getwaves 点击进入getwaves 生…

理解Web登录机制:会话管理与跟踪技术解析(三)-过滤器Filter

在Java Web应用中,Filter(过滤器)是实现登录校验的常见方式。通过Filter,我们能够在请求到达实际的业务逻辑之前,对其进行拦截和处理,从而完成身份校验、权限验证等操作。本文将深入探讨登录校验的实现方法…

FreeBSD将操作系统支持时间从5年缩短为4年 继续与AMD合作

FreeBSD 项目今天发布了 2024 年第三季度进度报告,概述了该开源 BSD 操作系统在上一季度的改进情况。FreeBSD 开发人员仍然非常忙碌,他们在 2024 年第三季度取得的一些成就包括: FreeBSD 发布团队决定将支持时限从五年缩短为四年。 AMD 与 F…

kafka如何获取 topic 主题的列表?

大家好,我是锋哥。今天分享关于【kafka如何获取 topic 主题的列表?】面试题?希望对大家有帮助; kafka如何获取 topic 主题的列表? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在Kafka中,可以…

【网络-交换机】生成树协议、环路检测

路由优先级 路由优先级决定了在多种可达的路由类型中,哪种路由将被用来转发数据包。路由优先级值越低,对应路由的优先级越高,优先级值255表示对应的路由不可达。一般情况下,静态路由的优先级为1,OSPF路由优先级为110&a…

基于Spring Boot的在线装修管理系统的设计与实现,LW+源码+讲解

摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱,出错率高,信息安全性差&#…

【数字图像处理】图像旋转中三种常见插值方法的效果比较:最近邻插值、双线性插值和双三次插值

引言 插值是一种数学方法,用于在已知的数据点之间估计新的数据点。在图像处理中,插值通常用于图像缩放、旋转和其他形态变换。 原始图像 最近邻插值(Nearest-neighbor interpolation) 这是最简单的插值方法,也是计算…

“方块兽神仙猿点石成金”游戏搭建开发

“方块兽神仙猿点石成金”是一款结合了策略和运气的休闲游戏。玩家需在规定时间内向不同的山头投入矿石,等待神仙猿降临并随机选择一座山进行“点石成金”。根据神仙猿的选择,玩家将获得不同的奖励。 游戏核心机制 矿石投入:玩家在游戏开始…

C/C++每日一练:实现选择排序

选择排序 选择排序是一种简单直观的排序算法,时间复杂度为,其中 n 是数组长度,不适合大数据集的排序,适合于元素较少且对性能要求不高的场景。 选择排序的基本思想是:每次从未排序部分选择最小的元素,将其放…

Java8新特性/java

1.lambda表达式 区别于js的箭头函数,python、cpp的lambda表达式,java8的lambda是一个匿名函数,java8运行把函数作为参数传递进方法中。 语法格式 (parameters) -> expression 或 (parameters...) ->{ statements; }实战 替代匿名内部类…

《现代网络技术》读书笔记:SDN数据平面和OpenFlow

本文部分内容来源于《现代网络技术:SDN,NFV,QoE、物联网和云计算:SDN,NFV,QoE,IoT,andcloud》 SDN数据平面 SDN 数据平面也称为基础设施层,而在ITU-T的Y3300标准中则称为资源层,它是网络转发设备根据 SDN控制平面的决策来执行数据…

linux centos 安装redis

安装 wget https://download.redis.io/releases/redis-7.4.0.tar.gz解压redis-7.4.0.tar.gz文件 tar -zxvf redis-7.4.0.tar.gz进入redis安装目录 cd redis-7.4.0make时报错,因为需要安装gcc,gcc安装需要联网安装 修改端口 编辑文件用vi。nano命令cen…

面向对象技术简述(含设计模式)

6.9.2 面向对象技术 面向对象 对象 分类 继承 通过消息的通信 面向对象 对象 分类 继承 通过消息的通信 面向对象对象分类继承通过消息的通信其中包括: 对象 运行的实体;既包含属性/数据,又包含方法/行为/操作数据的函数;…

yakit中的规则详细解释

官方文档 序列前置知识之高级配置 | Yak Program Language 本文章多以编写yaml模版的视角来解释 规则一览 匹配器 在编写yaml中会使用到这里两个东西 点击添加会在返回包的右下角出现匹配器 上面有三个过滤器模式,官方解释 丢弃:丢弃模式会在符合匹配…

算法每日双题精讲——双指针(移动零,复写零)

🌟快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 🌟 别再犹豫了!快来订阅我们的算法每日双题精讲专栏,一起踏上算法学习的精彩之旅吧!💪…

【SpringCloud】Kafka消息中间件

Kafka Kafka消息中间件对比:kafka介绍安装教程:配置以及启动顺序: Kafka整合微服务初级入门测试: Kafka整合SpringBoot①导入spring-kafka依赖信息②消息生产者③消息消费者Postman测试 Kafka 消息中间件对比: 消息中…

ViT模型复现项目实战

项目源码获取方式见文章末尾! 600多个深度学习项目资料,快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于CNN-RNN的影像报告生成】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模型实现…