我们使用Handler其实就是就是2个功能:
- 发送消息;
- 处理消息;
但是在这之前 ,我们要做好准备工作,那就是必须得有Looper和MessageQueue才行。
Handler就类似一个快递站,如果你没有对应的 运输设备(Looper) 和存储仓库(MessageQueue),就没办法运行啊。
1. 使用Handler的前提条件
通过源码,我们发现Handler其实有很多构造函数,但是最终都指向一个:
public Handler(@Nullable Callback callback, boolean async) {
// 此处省略监听 内存泄漏 的代码...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我们可以创建Handler都是要先获取到Looper 和 MessageQueue 的.
2. Handler发送消息
消息的发送主要分为三种发送:
- 普通发送;
- 定时发送;
- 延时发送;
2.1 send方式
这些是Handler类关于sendMessage的方法。
不得不感慨一下,如果使用Kotlin重写Handler,就不会存在这么多的重载方法啦。
那么Handler是如何处理三者(普通,定时,延时)的关系呢?这里不妨可以思考一下…
具体看一下源码的设计,验证一下我们的猜想:
2.1.1. 普通发送:sendMessage()
通过延时发送(sendMessageDelayed),设置延时为0来实现即时发送的。
public final boolean sendMessage(@NonNull Message msg) {
// 注意这里,通过调用延时0发送,即:立刻发送
return sendMessageDelayed(msg, 0);
}
2.1.2. 延时发送:sendMessageDelayed()
具体的通过定时发送(sendMessageAtTime) 来实现的延时功能
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 这里:调用的定时发送—— 当前时间 + 延时时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
2.1.3. 定时发送:sendMessageAtTime()
消息在这里进行入队列操作,强调一点,真正的入队是由MessageQueue操作的。
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
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);
}
2.2 post方式
我相信大家在开发过程中,使用post() 的情况可能会更多一些吧。post 方式我们在使用过程上基本上都是与Runnable打交道,并未见到Message的身影。
难道Handler的消息机制不适用于post方式?今天我们去一探究竟。
先看一下使用最多的post()方法吧。
2.2.1. post()方法
public final boolean post(@NonNull Runnable r) {
// 这里有个getPostMessage()方法
return sendMessageDelayed(getPostMessage(r), 0);
}
2.2.2. getPostMessage()
该方法给我们返回Message,将我们的Runnable赋值给Message的callback属性。关于Message的更多内容,可以查看 Handler的前世今生3——Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
通过getPostMessage() 返回对应的Message对象,无缝对接 send方式。本质最后都是通过**sendMessageAtTime()**将消息入队。
2.3 消息的入队操作
强调一下:入队操作的主角是MessageQueue
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 标示一下,这是我的消息
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 这里才是真正的入队噢...
return queue.enqueueMessage(msg, uptimeMillis);
}
2.4 消息的复用obtain()
可以看到,消息的复用本质上还是Message来做的,这里仅仅是做了一层封装,毕竟Handler才是Message的使用者。
public final Message obtainMessage()
{
return Message.obtain(this);
}
3. Handler 处理消息
我们都知道Handler是自产自销,接下来我们就来讨论一下Handler 是如何处理消息的。
我们知道在Looper的loop()方法中,handler调用了其dispatchMessage()
/**
* 最常用的方式
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
/**
* 继承Handler时重写该方法
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
// post方式,则最终还是在post的runnable中执行
if (msg.callback != null) {
handleCallback(msg);
} else {
// send方式,创建Handler中是否传入Callback,对应进行处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
// 妙啊...
message.callback.run();
}
可以看到消息处理操作是在Handler中进行的。消息出队列是在Looper中的loop()中进行的。
完美演绎啊,不得不感慨一下,这个设计真是太棒啦…
4. Handler的清理操作
在实际开发中,我们一般都会在Activity要销毁时,调用removeCallbacksAndMessages() 方法。
Remove any pending posts of callbacks and sent messages
其实就是MessageQueue清除post的回调和send的消息。
public final void removeCallbacksAndMessages(@Nullable Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
5. 总结
- Handler的发送消息(post,send)最终都是sendMessageAtTime() 实现。
- post()是根据getPostMessage(),将Runnable 和 Message 进行绑定的。
- 消息的入队操作都是靠enqueueMessage()
- 在dispatchMessage() 分情况处理消息。
到这里,其实我们会发现,关于Message的重要属性 taget , callback ,mAsynchronous 都已经在Handler中完成赋值,唯有 when属性 没有在Handler进行处理,奇哉怪哉…
希望大家能继续查看:Handler的前世今生5 —— MessageQueue