Android开发线程间的交互之Handler的学习

2024-08-28 16:58

本文主要是介绍Android开发线程间的交互之Handler的学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、handler的定义

UI线程用于UI的展示交互,子线程用于耗时操作,不能更新UI。handler 主要接受子线程发送的数据, 并用此数据配合主线程更新UI。

二、handler的简单用法。

new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);Message msg = new Message();msg.obj = "你好!";mHandler.sendMessage(msg);} catch (Exception e) {e.printStackTrace();}}
}).start();
Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg) {textView.setText((String)msg.obj);};
};

三、为什么Handler

主要是考虑到多线程并发的问题

四、Handler的机制

涉及到的类:

Looper   MessageQueue    Message  Handler   ThreadLocal
1、ThreadLocal

ThreadLocal可以在不同的线程中维护一套数据的副本并且彼此互不干扰

2、looper

不管是在主线程还是在其他线程使用handler,首先需要创建Looper。

主线程使用Looper.prepareMainLooper();在activity创建的时候系统就会自动创建
其他线程使用需要手动创建Looper.prepare();

<1> 内部包含一个消息队列MessageQueue,接收handler发送的消息
<2> loop方法一个死循环,不断的从MessageQueue去取消息,如果有消息就处理,没有就等待。

3、Handler实现机制(源码分析)
图解
handler发送消息
Created with Raphaël 2.2.0 UI线程 创建handler 获取UI中对应的looper UI中是否创建looper 使用ThreadLocal get方法得到looper 得到looper中的MessageQueue Handler将消息发送到UI线程的消息队列MessageQueue中 UI创建looper set进ThreadLocal yes no
handler处理消息
Created with Raphaël 2.2.0 MessageQueue存储消息 looper.loop获取消息 调用handler处理消息 等待 yes no

<1> 在prepare()方法中会创建Looper对象和MessageQueue对象,并将Looper对象存放到ThreadLocal

<2> 调用Looper.loop()方法。方法中首先会通过Looper me = myLooper();得到Looper对象(myLooper方法中使用ThreadLocal.get()得到Looper对象)。然后开启一个for死循环,获取MessageQueue中的消息Message msg = queue.next(); 如果消息为空则堵塞, 不为空就调用msg.target.dispatchMessage(msg);将消息回调给handler的dispatchMessage方法内部会回调handleMessage方法

<3> 创建当前线程的handler的子类并实现handleMessage拿到msg消息。创建handler时会获取到Looper对象和MessageQueue对象:mLooper = Looper.myLooper();mQueue = mLooper.mQueue;

<4> 创建Message对象,并使用handler.sendMessage发送msg。sendMessage方法会调用mQueue.enqueueMessage()方法并将handler赋值给msg.target。mQueue.enqueueMessage()会将msg消息存放到MessageQueue队列中(内部是使用单链表存储)

<5> Looper.loop()方法会调用MessageQueue.next()方法去取出msg并将msg删除

注意:在子线程中手动创建Looper的话,在不需要的时候应该将Looper退出,防止子线程一直处于等待状态。Looper提供了quit()和quitSafely()来退出。区别就是quit直接退出,而quitSafely只是设定了一个退出标志,等消息队列中的消息处理完毕后才安全退出。

五、handler的内存泄漏以及解决办法

handler的内存泄漏

经常用到的handler的使用方法:

    Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};Message message = Message.obtain();message.what = 1;mHandler.sendMessageDelayed(message,10*60*1000);

在Java中,非静态内部类会隐性地持有外部类的引用,而静态内部类则不会。在上面的代码中,Message在消息队列中延时了10分钟,然后才处理该消息。而这个消息引用了Handler对象,Handler对象又隐性地持有了Activity的对象,当发生GC是以为 message – handler – acitivity 的引用链导致Activity无法被回收,所以发生了内存泄露的问题。

解决方法:
1、使用static

在创建handler的时候使用static,由于Handler不再持有外部类对象的引用,导致程序不允许在Handler中操作Activity中的对象了。所以需要在Handler中增加一个对Activity的弱引用(WeakReference)。

static class MyHandler extends Handler{WeakReference<Activity> weakReference;MyHandler(Activity activity){weakReference = new WeakReference<>(activity);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);final Activity activity = weakReference.get();if (activity != null) {((TestHandlerSendActivity)activity).mTextView.setText("我是handler—send更新的");}}
}
2、及时清除消息

正是因为被延时处理的 message 持有 Handler 的引用,Handler 持有对 Activity 的引用,形成了message – handler – activity 这样一条引用链,导致 Activity 的泄露。因此我们可以尝试在当前界面结束时将消息队列中未被处理的消息清除,从源头上解除了这条引用链,从而使 Activity 能被及时的回收。

六、总结:

Handler负责发送消息,Looper负责接收发送的消息,并将消息回传给handler自己。MessageQueue是存储消息的容器。

这篇关于Android开发线程间的交互之Handler的学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1115351

相关文章

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32