当前位置: 首页 > news >正文

【Android】使用Handler做多个线程之间的通信

在这里插入图片描述

一:两个线程之间的通信

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    private void startHandler(){new Thread(new Runnable() {@Overridepublic void run() {
//                //准备当前线程的looper
//                Looper.prepare();
//                //获取当前线程的实例
//                Looper looper = Looper.myLooper();
//
//                //在子线程中创建Handler,如果不传looper,默认是和子线程关联,子线程中去更新UI就会报错
//                Handler handler1 = new Handler();
//
//                Looper.loop();//开始循环发送消息// 有传Looper.getMainLooper(),表示和主线程关联Looper mainLooper = Looper.getMainLooper();Handler handler1 = new Handler(mainLooper);String id = etUserId.getText().toString();String urlAddress = "http://titok.fzqq.fun/addons/cms/api.user/userInfo?user_id=" + id + "&type=archives";try {URL url = new URL(urlAddress);HttpURLConnection connection = (HttpURLConnection)url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(8000);connection.setReadTimeout(8000);InputStream inputStream = connection.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));StringBuilder builder = new StringBuilder();String line;while((line = reader.readLine() ) != null){builder.append(line);}//返回主线程更新UIhandler1.post(new Runnable() {@Overridepublic void run() {String result = builder.toString();Log.i(TAG, "run: 网络访问的结果是:" + result);}});connection.disconnect();} catch (MalformedURLException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}}).start();}

四种情况

  • Handler在哪个线程中创建,它就属于哪个线程

1:利用主线程Handler更新UI

之前更新UI信息是调用runOnUiThread()方法,今天学习到第二种方法,主线程中创建Handler(这里是成员变量),再在子线程中利用handler.post()方法更新UI

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2:子线程中的Handler不传Looper

在子线程中创建Handler,进行UI更新就会报错;

因为这里的handler.post()是post给子线程了,子线程不可以更新UI的,所以报错

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3:子线程中的Handler设置Looper

非要在子线程中进行post请求,就需要去获取子线程的looper

调整指定在子线程中进行handler的post请求,成功,没有日志打印(正常)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4:子线程利用主线程的Looper

子线程的Handler中传入主线程的looper也是可以正常在主线程中进行UI更新的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二:一些细节

1:Looper.prepare

作用:为当前线程创建 Looper 并初始化消息循环相关的基础设施 。在 Android 中,主线程(UI 线程)的 Looper 是系统自动初始化好的,但子线程若要使用 Handler 进行消息循环,就必须手动调用 Looper.prepare()

内部逻辑:它会在当前线程中创建一个 Looper 对象,同时该对象内部会维护一个 MessageQueue(消息队列),用于存储后续发送过来的 Message(消息) 。如果在已经存在 Looper 的线程中再次调用 Looper.prepare() ,会抛出 RuntimeException ,保证一个线程最多只有一个 Looper

2:Looper.myLooper();

作用:获取当前线程关联的 Looper 对象 。如果当前线程还未通过 Looper.prepare() 初始化 Looper ,那么调用此方法会返回 null 。一般在需要明确拿到当前线程 Looper ,用于一些和 Looper 关联的操作(比如给 Handler 构造方法传特定 Looper 等场景 )时使用,不过很多时候如果只是在当前线程创建 HandlerHandler 内部会自动去获取当前线程的 Looper ,不一定需要显式调用这个方法来获取。

拓展:Looper.myLooper.quit()方法是退出线程,也有.quitSafely()安全退出这一说

3:Looper.loop()

作用:开启 Looper 的消息循环,让当前线程进入一个不断从 MessageQueue 中取出消息并处理的循环过程 。

总结:Looper相当于可以往容器里放Message的工具,Handler相当于一个放Message的容器,.loop方法不断从队列里取出消息交给对应的Handler,Handler调用handleMessage方法处理消息

  • 普通线程执行完 run () 方法后就会终止,无法再次使用
  • 加入 Looper 后,Looper.loop () 会启动一个无限循环,让线程一直运行
  • 这个循环会不断从 MessageQueue 中取出消息并处理
  • 当你需要使用这个线程时,只需通过 Handler 发送消息即可
  • 线程会一直处于等待 - 处理消息的状态,直到调用 quit () 方法才会真正结束

三:多个线程间的通信

场景:(多对一)多个线程发送消息,一个线程来接收

1:延迟发送消息

    private Handler handler = new Handler(Looper.getMainLooper());
 else if (v.getId() == R.id.btn_post_delayed) {delayToast();
private void delayToast() {handler.postDelayed(new Runnable() {@Overridepublic void run() {Toast.makeText(HandlerActivity.this, "我他喵延迟1s来啦", Toast.LENGTH_SHORT).show();}},1000);}

效果如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2:处理消息队列

如果有多个子线程和主线程需要处理同一件事情的话,我们可以利用同一个Handler,这样的代码也会更加直观和清爽,对底层的性能消耗也会更小一点

(1)写法一

一般要去指定它是一个主线程,不传参的写法是已经过时的写法;

如果还需要处理一些其他的事情,只有一个handleMessage不够,那就这种写法

    /*** 第一种处理接收消息的写法* 记得要传参,不传参的过时了*/private Handler sendHandler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);}};

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)写法二

new 一个Handle.Callback()接口,重写handlerMessage方法

接收消息的一方

    private Handler sendHandler1 = new Handler(new Handler.Callback(){@Overridepublic boolean handleMessage(@NonNull Message msg) {switch(msg.what){case 123:String data = (String) msg.obj;Toast.makeText(HandlerActivity.this, data, Toast.LENGTH_SHORT).show();return true;//回收Messagecase 234:int num = (int) msg.obj;Toast.makeText(HandlerActivity.this, num+"", Toast.LENGTH_SHORT).show();return true;}return false;}});

返回ture底层会判断出当前消息已经被消费过了,就可以被回收掉了

发送消息的一方

1:Message详解

通信的桥梁就是Message

  • message.what 标记消息是从哪里被发送过来的,类似于请求码
  • message.obj Message中的obj,就是Object,海纳百川有容乃大,传什么类型都可以接受,再把这些数据发送出去

startTaskA和B两种发送消息的方式最大的不同就在于,创建Message的方式;

区别:new Message()是每次都去创建一个Message,代价太大;

obtatin(obtain 英 [əbˈteɪn] 得到)是从消息池中获取一个Message对象,可以减小创建对象的开销,更适合性能要求高的地方

Message message = new Message();
Message message = sendHandler1.obtainMessage();

以下是详细代码

    private void startTaskA(){new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}String data = "我是服务器返回的数据:{name = 我他喵莱纳}";//创建一个Message,通过sendHandler1对象发送消息Message message = new Message();message.what = 123;message.obj = data;sendHandler1.sendMessage(message);}}).start();}
    private void startTaskB(){new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}Message message = sendHandler1.obtainMessage();message.what = 234;message.obj = 520;sendHandler1.sendMessage(message);}}).start();}

四:post通信和Message通信对比

在HandlerMessage中处理通过sendMessage方法发送的消息,可以集中处理多个线程发来的消息;有很多任务需要同时去处理,需要把它写在一起,就用what进行区分

post更适合一对一线程之间的通信,代码逻辑更简单,不需要数据的传递;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

http://www.xdnf.cn/news/1415935.html

相关文章:

  • 【Flask】测试平台开发,应用管理模块实现-第十一篇
  • 【lucene核心】impacts的由来
  • 旧物回收小程序:科技赋能,开启旧物新生之旅
  • 山东省信息技术应用创新开展进程(一)
  • 《C++进阶之STL》【红黑树】
  • OS+MySQL+(其他)八股小记
  • 【macOS】垃圾箱中文件无法清理的常规方法
  • 应用平台更新:可定制目录、基于Git的密钥管理与K8s项目自动化管理
  • Qt中的信号与槽机制的主要优点
  • LeetCode 142. 环形链表 II - 最优雅解法详解
  • 阿里云代理商:轻量应用服务是什么?怎么用轻量应用服务器搭建个人博客?
  • Linux性能调试工具之ftrace
  • JSP 输出语法全面解析
  • 制造业生产线连贯性动作识别系统开发
  • MCP SDK 学习二
  • 【开题答辩全过程】以 基于Java的网络购物平台设计与实现为例,包含答辩的问题和答案
  • 集合-单列集合(Collection)
  • Docker中使用Compose配置现有网络
  • Ubuntu 中复制粘贴快捷键
  • LeeCode 37. 解数独
  • 【嵌入式】【电机控制】基础知识列表
  • PS自由变换
  • Dreamore AI-解读并描绘你的梦境
  • ARM裸机开发(基础汇编指令)Day02
  • 【AI智能体】LLM记账智能体+MCP服务-实现步骤与效果展示
  • 分布式锁和分布式事务
  • 使用yt-dlp下载网页视频
  • 国内大型银行数据模型实践案例
  • 2025年跨领域职业发展证书选择指南
  • 漫谈《数字图像处理》之基函数与基图像