Android消息传递机制Handler完全解析之1基础介绍
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数据处理和类流程图:
我们实际代码中,一般只用到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是可以有多个的。
图解:
四、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
这个几年前写的简单总结了,有简单的示例代码,里面的总结还是有点瑕疵!