花样使用Handler与源码分析
作者:Zy_JiBai 发布时间:2023-07-30 08:36:31
前几天在跟公司大佬讨论一个问题时,看到他使用Handler的一种方式,旁边的同事在说:以前不是这么用的啊。这个问题引发了我的好奇,虽然当时翻清楚道理了,但是还是想给大家分享一下。
Handler在之前也说到过他的使用以及源码分析,而且相信大家都知道如何使用它,最常见的使用方法恐怕就是下面这种了:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
这种情况会有一个问题:我们都知道Handler是可以用在子线程给主线程更新的,当子线程给主线程回调时,主线程中的Handler通过接收发送过来的对应消息,去执行对应的任务。而对于上面这个Handler对象,如果他是主线程中的,那么我们子线程中需要拿到主线程的这个Handler对象。
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
new Thread(new Runnable() {
@Override
public void run() {
handler.sendMessage(new Message());
}
}).start();
但是上面这种写法实在是太不好看了,而且handler还是一个局部变量,在其他方法中也无法使用。
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "onCreate: " );
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
new Thread(new Runnable() {
@Override
public void run() {
handler.sendMessage(new Message());
}
}).start();
}
这个看上去应该就好多了,可能也是大多数人的一种写法。
其实说白了,如果说我们要在子线程中给主线程/相应线程回调,那么一定要拿到主线程中的Handler的索引。这么说就很直接了,可能有些情况下无法拿到主线程/相应线程的Handler,或者拿到的方法很麻烦:
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
//执行相关的耗时操作,然后结束后通过Handler回调给主线程
}
}).start();
}
}
现在我有这样的需求:创建一个服务,在服务中开启一个子线程执行耗时操作,当执行完毕后回调在主线程中相应。这种情况下想要拿到主线程的Handler对象也不是不可以,方法还是有很多,把主线程的handler写成static、创建类继承Handler并且序列化,然后通过intent传入.....可能还有其他的一些方法,但是就目前的这些情况来看,貌似都不是很友好。下面给大家带来一种比较优雅且方便的方法:
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
/*执行相关的耗时操作,然后结束后通过Handler回调给主线程*/
new Handler(Looper.getMainLooper()).sendMessage(new Message());
}
}).start();
}
}
只有一句话:又方便看着又舒服。
通过Looper.getMainLooper方法,可以获取到主线程的Looper对象.
虽然之前说我们需要主线程中创建的Handler,其实严格的说是不对的。究其根本是因为主线程已经为他自己加载了mainLooper,而我们在主线程中new Handler,会默认获取主线程的Looper引用。
public static void main(String[] args) {
//pass
Looper.prepareMainLooper();
//pass
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
public Handler(Callback callback, boolean async) {
//pass
mLooper = Looper.myLooper();//在主线程中new的Handler获取到的looper就是主线程的mainLooper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//pass
}
现在看来就很明确了,在主线程中创建Handler只是个幌子,真正在背后操纵一切的其实是looper对象。所以只需要让Handler的mLooper引用获取到主线程的引用就好了。
而且Looper.getMainLooper方法是外部可见的,大胆猜测这个方法就是为了这种方便的写法而存在的。我们可以通过这个方法获取到主线程的looper,让他实现主线程中接收回调。
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
但是注意我们上述的这种写法:new Handler(....).sendMessage
这种写法不管你怎么去实现,他无法在主线程得到回应(是给大家挖了个坑哈哈),原因很简单:没有重写Handler.handlerMessage方法。
在使用Handler接受消息时有三种方式:
重写Handler.handlerMessage方法,在该方法中接收
在Handler构造器中实现Callback接口,在回调接口中接收
不做任何处理,但是使用post方式发送消息。
在之前我们Handler接收消息见到的几乎都是handleMessage方法,其实这只是其中一种方法,在执行该方法之前会有一个分发的方法dispatchMessage:
/**
* Handle system messages here.
**/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//msg中的callback,这个是通过post方法自己封装的msg(自行查源码),优先级是最高的
handleCallback(msg);
} else {//或者在构造器中实现Handler的Callback接口,这个优先级第二
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//这才是我们之前最常用的方法,最低的优先级
}
}
可以看到在handlerMessage方法之前还有两种回调的方法。在上述案例中我们并没有重写第三种方法,所以对于在子线程中匿名使用Handler的情况,我们可以采取上述两种方案。代码就不写了,大家都是聪明人。
好了关于Handler 的更多使用就到这里了,喜欢的朋友希望多多支持。
来源:https://blog.csdn.net/zy_jibai/article/details/81583397


猜你喜欢
- object nullObj = null; object obj = new Object(); return nullObj ?? ob
- 介绍单例模式是软件工程学中最富盛名的设计模式之一。从本质上看,单例模式只允许被其自身实例化一次,且向外部提供了一个访问该实例的接口。通常来说
- Feign使用@RequestLine遇到的坑如何在微服务项目中调用其它项目的接口试使用spring cloud feign声明式调用。/*
- 前言Java8 由Oracle在2014年发布,是继Java5之后最具革命性的版本。Java8吸收其他语言的精髓带来了函数式编程,lambd
- ModbusModbus是一种串行通信协议。Modbus 一个工业上常用的通讯协议、一种通讯约定。Modbus协议包括RTU、ASCII、T
- java中的interface接口实例详解接口:Java接口是一些方法表征的集合,但是却不会在接口里实现具体的方法。java接口
- 准备:(1) IDEA 2021(2)Java 1.8(3)数据库 MySQL 5.7 (SQLyog 或 Navicat)在 MySQL
- Viewpager通俗一点讲就是一个允许左右翻转带数据的页面的布局管理器,经常用来连接Fragment,它很方便管理每个页面的生命周期,使用
- 本文基于jdk1.8进行分析。LinkedList和ArrayList都是常用的java集合。ArrayList是数组,Linkedlist
- 一. String对象的比较1. ==比较是否引用同一个对象注意:对于内置类型,==比较的是变量中的值;对于引用类型 , == 比较的是引用
- 本文实例为大家分享了SpringMVC使用MultipartFile实现文件上传的具体代码,供大家参考,具体内容如下一、配置文件Spring
- springboot 启动排除某些bean的注入问题:最近做项目的时候,需要引入其他的jar。然后还需要扫描这些jar里的某些bean。于是
- 为开发团队选择一款优秀的MVC框架是件难事儿,在众多可行的方案中决择需要很高的经验和水平。你的一个决定会影响团队未来的几年。要考虑方面太多:
- 本文我们将要讨论Java面试中的各种不同类型的面试题,它们可以让雇主测试应聘者的Java和通用的面向对象编程的能力。下面的章节分为上下两篇,
- 小伙伴私信我说想要研究下Spring的源码,想让我出一期教程来实现IDEA导入Spring源码,今天它来了~版本 :IDEA 2020.2.
- 最近碰到这个问题,在使用spring提供的JpaTemplate进行查询时,如果数据量超过100 条,查询效率就会明显降低。由于开始时使用J
- 用来记录自己所用到的知识前两天在做项目的时候发现有时候在访问网络数据的时候由于后台要做的工作较多,给我们返回数据的时间较长,所以老大叫我加了
- 本文主要介绍Android实现拍照、录像、录音代码的资料,这里整理了详细的代码,有需要的小伙伴可以参考下。RecordActivity.ja
- 场景File与FileStream的区别举例:将读取文件比作是从A桶往B桶运水。使用File就是整个用桶倒进去,使用FileStream就是
- 说明:在填写表数据时当输入完一个文本框后,输入下一个文本框时需要用Tab键切换,但是有的人喜欢用Enter键切换下一个,此方法是Enter取