Handler已经是个老生常谈的问题了,对于Android开发者来说Handler是一个无论面试还是日常开发都无法避开的话题,因为它是Android系统中最重要的事件分发和处理机制。本篇文章就Handler从使用到源码解析再到使用上可能产生相应的问题做一个总结。
一、Handler基础
1、初识Handler
相信许多小伙伴刚了解Handler的时候是在线程间通信上认识的。比较常用的当一个功能在子线程执行出结果需要在控件上展示的时候,由于在一般情况下子线程不允许更新View,所以需要将子线程的结果提交给主线程,由主线程完成数据的绘制,在这两个线程进行数据传递就需要Handler的帮助。
再后来根据Handler提供的方法特性,我们可以用来做延迟加载或者开发一个自定义定时器等功能。
2、基础使用
Handler的使用比较简单:
1、定义Handler,接收消息的主体
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
//此方法用于接受消息,并进一步处理
super.handleMessage(msg);
//根据传入的what类型处理不同消息
switch (msg.what){
// case msgWhat1:
// break;
// case msgWhat2:
// break;
// ...
}
}
};
2、发送消息
Handler提供了多种发送消息的方法,根据不同需求执行不同方法
//通过复用的方式创建消息体
Message msg = mHandler.obtainMessage();
//发送消息
mHandler.sendMessage(msg)
//发送空消息
mHandler.sendEmptyMessage()
//延迟发送空消息
mHandler.sendEmptyMessageDelayed(what,time)
//延迟发送消息
mHandler.sendMessageDelayed(msg,time)
//发送一个特定时间执行的消息
mHandler.sendMessageAtTime(msg,time)
//发送一个特定时间执行的空消息
mHandler.sendEmptyMessageAtTime(what,time)
//发送消息并插入到队列前,立刻执行
mHandler.sendMessageAtFrontOfQueue(msg)
//除了send系列还有post系列的消息发送,区别在于post发送需要传入
//一个Runnable对象,执行完后不会执行到handleMessage方法中,而是
//直接回调到Runnable的run方法中,post提供的方法与send类似,不
//多做赘述
mHandler.post(new Runnable() {
@Override
public void run() {
//执行代码
}
});
二、Handler原理
1、Handler成员
Handler:消息的接收和发送主体,通过send/post发送消息,在handleMessage或者 Runnable中处理消息
MessageQueue:消息队列的管理者,虽然名字带着Queue,但是MessageQueue本身并不是队列结构体,真正的结构体是Message,它只是消息队列的管理者,同事内部持有当前线程的Message队列,对消息进行获取、插入、移除等操作
Message:消息主体,类内部通过Message类型的next变量将Message组成一个单向链表结构
public final class Message implements Parcelable {
//......
@UnsupportedAppUsage
/*package*/ Handler target;
@UnsupportedAppUsage
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
//指向下一条Message,形成链表
@UnsupportedAppUsage
/*package*/ Message next;
//......
}
Looper:轮询者,内部是一个无限循环,通过不断检测和轮询MessageQueue中是否有消息来分发到Handler的回调方法中。
2、执行流程
2.1 发送消息
以sendMessage为例:
//Handler.java
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
//MessageQueue直接取的成员变量
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//Message持有当前Handler对象
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//发送消息最终都会调用到MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
发送消息主要做了三件事
1、检测MessageQueue是否创建
2、通过msg.target = this 让Message持有当前Handler对象,方便后面调用
3、最终调用了MessageQueue的enqueueMessage方法
那MessageQueue.enqueueMessage()又做了什么呢?
//MessageQueue.class
/**
* 当前的消息队列
*/
@UnsupportedAppUsage
Message mMessages;
/**
* 将发送的消息进行排序,插入等处理
*/
boolean enqueueMessage(Message msg, long when) {
//是否持有了Handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//同步锁防止,同一个Hander在不同线程发送消息造成的同步插入问题
synchronized (this) {
//正在使用中的抛出异常 与markInUse配合
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//当前消息系统退出了 直接回收Message
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//改变使用标识位,与isInUse配合
msg.markInUse();
//更新Message时间,用于排列
msg.when = when;
//获取队列的头指针
Message p = mMessages;
boolean needWake;
//如果当队列没有消息 ||
//如果当前消息时间为0,为立即执行的消息放在队首 ||
//当前消息执行时间还小于队列中最近需要执行的消息
if (p == null || when == 0 || when < p.when) {
//将当前消息作为队列的头指针
msg.next = p;
mMessages = msg;
//如果阻塞则需要唤起,否则等待loop的轮询执行
needWake = mBlocked;
} else {
// 插入到队列中间。通常我们不需要唤醒事件队列,除非在队列
//的头部有一个障碍,并且消息是队列中最早的异步消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//遍历当前队列插入到队列中间,队列插入算法
for (;;) {
prev = p;
p = p.next;
//查找第一个时间大于当前消息的Message