欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android消息传递机制Handler完全解析之1基础介绍

程序员文章站 2022-05-13 22:05:22
...

Android消息传递机制Handler完全解析之基础介绍

本文对Handler进行基础介绍,Handler是什么?为什么使用Handler?如何使用Hanadelr?Handler里面主要的类有哪些?Handler从发送消息到接收消息的流程是怎样的?

一、Handler是什么?

Handler是一个消息分发对象。
Handler整个框架包含了一套消息处理机制,我们可以发消息,也可以通过它处理消息。

Android还有一个事件分发机制TouchEvent,为了不搞混,Handler说出消息传递机制好记一些。

二、为什么要使用Handler

1、在主线程更新UI界面

我们常用的是在主线程更新UI界面,子线程不能直接更新UI界面,只能在子线程发送消息,主线程接收后更新UI

比如网络请求需要子线程,在网络请求的回调中,数据不能马上显示更新到UI上,需要通过Handler传递。

2、最根本的目的就是为了解决多线程并发的问题

Android提供Handler消息传递机制,根本目的就是为了解决多线程并发的问题。
打个比方,如果在一个activity中有多个线程,并且没有加锁,就会出现界面错乱的问题。
但是如果对这些更新UI的操作都加锁处理,又会导致性能下降。
处于对性能的问题考虑,Android给我们提供这一套更新UI的机制我们只需要遵循这种机制就行了。
不用再去关心多线程的问题,所有的更新UI的操作,都是在主线程的消息队列中去轮训的。
无论多少个消息,哪怕是同时发送,但是内部会对每个消息进行有序处理,最总消息会有序回调。

了解Handler了的作用,接下来就是学习Handler里面有哪些类和方法,如何使用Handler

三、Handler相关的主要类

Android的消息处理有三个核心类:Looper,Handler和Message。
其实还有一个MessageQueue(消息队列MQ),但是MQ被封装到Looper里面了。

Handler数据处理和类流程图:

Android消息传递机制Handler完全解析之1基础介绍

我们实际代码中,一般只用到Handler和Message对象,
如果是需要在子线程中创建Handler对象并接收消息,那么Looper也要用到,这个后面会介绍。

1、线程的魔法师 Looper

Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓 Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。

每一个线程只有一个Looper,每个线程在初始化Looper之后,然后Looper会维护好该线程的消息队列,
用来存放Handler发送的Message,并处理消息队列出队的Message。
它的特点是它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,
所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,
从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。

为什么我们使用Handler的时候从来都不需要创建Looper呢?
这是因为Activity在创建的时候已经帮我们创建Looper了,
在主线程中,ActivityThread默认会把Looper初始化好,prepare以后,当前线程就会变成一个Looper线程。

如果在子线程中创建Handler就要自己创建自己的Looper。

2、异步处理大师 Handler

Handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),
即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务 (handleMessage),整个过程是异步的。

也就是说发送消息是不会影响接收消息的。

handler创建时会关联一个looper。

3、MessageQueue 消息队列

MessageQueue是一个消息队列,用来存放Handler发送的消息。
每个线程最多只有一个MessageQueue。MessageQueue通常都是由Looper来管理,
而主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。
其他非主线程,不会自动创建Looper。

4、Message类

Message是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。
Message被 存放在MessageQueue中,一个MessageQueue中可以包含多个Message对象,
Message中 包含了两个额外的 int字段和一个object字段,这样在大部分情况下,使用者就不需要再做内 存分配工作了。
虽然Message的构造函数是public的,但是最好是使用Message.obtain()或Handler.obtainMessage()函数来获取Message对象,
因为 Message的实现中包含了回收再利用的机制,可以提供效率。

Message类定义的变量和常用方法如下:

(1)public int what:变量,用于定义此Message属于何种操作
(2)public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
(3)public int arg1:变量,传递一些整型数据时使用
(4)public int arg2:变量,传递一些整型数据时使用
(5)public Handler getTarget():普通方法,取得操作此消息的Handler对象。

Handler相关类的个数

一个线程可以有多个Handler,但是只能有一个Looper!
每个线程最多只有一个MessageQueue。
每个MessageQueue排队的消息Message是可以有多个的。
图解:
Android消息传递机制Handler完全解析之1基础介绍

四、Handler的用法

1、Handler的相关发送方法


1.post(Runnable)
2.postAtTime(Runnable,long)
3.postDelayed(Runnable,long)
4.sendEmptyMessage(int)
5.sendMessage(Message)
6.sendMessageAtTime(Message,long)
7.sendMessageDelayed(Message,long)

其中,有AtTime的表示在某个时间发送,Delayed表示在当前时间延迟多久放送,其他表示立即发送。
这里的发送只是发送消息到MessageQueue。

光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象。
这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了。
使用post(Runnable)方法后,子线程被系统调用启动线程,不需要我们去start。

2、Handler接收消息

Handler接收消息非常简单:

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //接收数据
        }
    };


如果Handler对应的Looper在主线程创建,那么Handler消息处理就是主线程,消息回调的地方就是主线程,可以直接更新UI;

如果Handler对应的Looper在子线程创建,那么Handler消息处理就是子线程,消息回调的地方就是子线程,不能直接更新UI。

Handler构造方法是可以传入Looper对象的,那么没传入Looper参数,那么Handler在什么线程创建,对应的Looper就是在什么线程。

下面是在主线程创建Handler,传入子线程Looper的示例代码:


    private void initHandler() {
        // 创建Handler线程,子线程
        HandlerThread thread = new HandlerThread("child1");
        // 做基本准备
        thread.start(); // 源码里面执行了Looper.prepare()和Looper.loop()
        // 获取循环者
        Looper looper = thread.getLooper();
        // 绑定Handler和Looper
        Handler handler = new Handler(looper) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //子线程接收消息
            }
        };
    }


Handler发送消息是主线程和子线程都可以的,但是回调消息handleMessage只跟对应的Looper同一个线程。

下面是Handler发送数据的一个简单片段示例:

	// 先创建Message对象
	Message msg = Message.obtain(); // Message.obtain();

    // 编辑需要存储的数据,可以是一个或者多个

	// what 一般用来表示消息类型
	msg.what = 1;

	//传递一些整型数据
	msg.arg1 = 100;
	msg.arg2 =200;
	
	//传递对象数据
	msg.object = CLassA ;

	// Message对象可以保存Bundle类型的数据
	Bundle data = new Bundle();
	data.putString("name", "李文志");
	data.putInt("age", 18);
	data.putString("sex", "男");
	// 把数据保存到Message对象中
	msg.setData(data);
	
	// 使用Handler对象发送消息
	handler.sendMessage(msg);

Handler发送完成后,经过MessageQueue处理完成后,
就能在handleMessage方法中获取到Message数据对象。

五、Handler从发送到接收的过程:

Handler从sendMessage到handleMessage接收数据,用到了监听事件和回调方法的思想。

整个大致过程的大概步骤如下:

1.创建一个Loop对象,用来管理MessageQueue

2.创建Handler对象,用来发送消息和接收回调消息

3.MessageQueue来接收和保存子线程发过来的消息

4.上面只是做好接收消息的准备,做好相关准备后,才会让子线程发送消息

5.直接调用Handler对象,通过Handler对象的sendMessage方法来发送数据

6.消息是发送到在MessageQueue对象中保存的

7.Loop控制MessageQueue传递消息给Handler对象,

这里就要注意了,虽然概念上说的是Handler能对子线程的数据进行接收和处理。
但实际上它是接收MessageQueue里面的数据,然后进行处理的,
MessageQueue里面可以接收很多很多的Message数据,它们以队列的形式排列,
当MessageQueue处理完一个数据后,会通知Handler,这时Handler就能在handleMessage接收到回调数据。

8.这是最后一步了,Handler对象在回调方法对传来的信息进行判断,并作相应的行为。

到这里Handler的基础已经讲解完了,学到这里其实已经能够在普通项目中正常使用了。

但是如果要深入了解里面的过程,还是要剖析Handler的相关源码。
比如,Handler发送延时消息和普通消息有什么区别,延时消息是如何实现的?
Looper如何循环工作的?
MessageQueue是如何管理Message的?

刚开始我也是猜测延时消息是里面做了定时器的处理,但是其实不是,是线程休眠。。。

下一个文章会深入的剖析Handler相关类的源码过程和原理。

本文没有编写完整的示例代码,如果想看,可以参考我之前写的总结代码:

https://blog.csdn.net/wenzhi20102321/article/details/52837834
https://blog.csdn.net/wenzhi20102321/article/details/530989437

这个几年前写的简单总结了,有简单的示例代码,里面的总结还是有点瑕疵!

共勉:只有不断行动,才能离目标越来越近。