前言
前面我们分析了Android消息机制中的消息Meesage和存储消息的队列MessageQueue,相信大家对这两个的实现已经很了解了吧!!!今天,咱们终于迎来了整个消息机制中的主角Handler,他的主要作用就是用来发送消息和处理消息的。
平时我们都是继承Handler,然后重写handlerMessage()
方法来处理消息,在需要发送消息的时候调用sendMessage()
或者postMessage()
来进行发送消息,然后内部什么的基本不用考虑,但是作为一个有责任有担当的高级技术开发者,怎么能忽略内部实现逻辑呢?
上篇讲述消息队列的时候,遗留两个问题没有进行讲解,今天就跟着笔者一起来 fuck the source code
- msg.target又是什么时候赋值的呢?
- 为什么msg.target == null的时候就是异步消息呢?
先从构造方法说起
从内部方法中可以看出,分为两种情况,参数带Looper和不带Looper,咱们就单独看下带Looper和不带Looper的构造方法分别做了什么
-
带Looper的构造方法
最终都调用了三个参数的构造方法,可以看出只是给成员变量进行赋值
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
-
不带Looper的构造方法
public Handler(@Nullable Callback callback, boolean async) { // 先拿到Looper对象,进行赋值 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对象,否则直接抛出异常,这也是为什么我们在子线程创建Handler对象的时候,先获取Looper对象的原因;但是为什么平时我们在主线程创建Handler的时候不需要调用
Looper.prepare()
方法来获取Looper对象呢???那是因为在程序ActivityThread的main函数中,已经默认帮我们创建了Looper对象了(详情可以去看看ActivityThread的源码)。构造方法我们就讲这么多,实际上也就这么些内容,关于Looper对象的获取,我们会在下篇文章里详细介绍。
接下来我们就来看看Handler关于获取消息和发送消息的相关操作
获取消息
在发送消息之前,我们首先得有Message对象,而Handler对象获取消息的操作,是通过
obtainMessage()
方法来获取,看到这里是不是很熟悉呢。没错,在我们之前讲解的Message里也有这个方法,那么Handler类里的这个方法做了什么操作呢,来咱们一起看下
@NonNull
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
咦,这不是直接调用了
Message.obtainMessage()
方法吗。没错,确实是这样的。详见【Handler系列一】Message对象的获取机制
既然消息已经获取到了,那么接下来就开始发送消息了。
发送消息
开发中我们经常用到的发送消息就是send...或者post...等系列方法来发送消息,通过源码查看,最终都调用的是
enqueueMessage()
方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 将自身赋值给msg.target
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这源码还是比较清晰的,首先将自身赋值给msg.target,其次调用MessageQueue的enqueueMessage()方法来将消息插入到消息队列中。关于消息队列如何处理消息,详见【Handler系列二】 MessageQueue
这里就解开了我们开头所说的问题1,msg.target就是在发送消息的将自身handler赋值给该消息的target变量的
发送消息还是比较简单的,那既然发送完消息了,是不是就到处理消息了呢?
处理消息
handler的处理消息是通过
dispatchMessage()
这个方法来实现的,那我们就来扒一扒到底是怎么实现的
/**
* Handle system messages here.
* 该方法注释就说明了 这里是处理系统消息的方法
*/
public void dispatchMessage(@NonNull Message msg) {
// 情况一:如果msg的callback变量不为null,则直接调用handlerCallback()方法
if (msg.callback != null) {
handleCallback(msg);
} else {
// 情况二:构造方法里进行赋值的Callback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 情况三:也是我们经常用到的
handleMessage(msg);
}
}
从源码可以看出三种情况,不同的处理消息方式,接下来笔者带着大家从这三种情况分别来分析分析
情况一:msg的callback变量不为null
private static void handleCallback(Message message) {
message.callback.run();
}
- Message的callback是通过Message.obtain()方法里赋值或者直接通过Message里setCallback里进行赋值的,然后直接调用进行处理。
- 这种处理消息的方式正常开发中基本没用到过
情况二:mCallback对象不为null
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);
}
- mCallback对象也是在handler的构造方法里传进来的,如果初始化的时候传入的话,直接通过这个接口的重写方法handleMessage进行处理消息
- 日常开发中用的也少
情况三:msg.target和mCallback都为null
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
- 这里的handleMessage为空方法
- 这就是我们日常开发中经常在创建Handler对象的时候进行覆写的方法,经常用到
移除消息
Handler中所有的removeMessage()和removeCallback()最终都是调用mQueue.removeMessage或者mQueue.removeCallback()来实现的,这里在上篇文章已经详细介绍了,详见【Handler系列二】 MessageQueue
msg.target什么情况下为null呢?
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
}
在MessageQueue插入消息的时候,首先判断msg.target是否为null,为null直接抛出异常;那为什么msg.target还有可能为null呢???这里就继续分析,msg.target什么时候没有被赋值呢?
//开启同步屏障
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
//新创建一个Message对象
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
// invariant:
p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
这里可以看出,在开启同步屏障的时候,创建了一个Message对象,这里没有给msg.target进行赋值,所以这时候msg.target为null,然后将该消息也插入到消息队列里,所有消息队列里就会有一个msg.target为null的消息
总结
- Handler主要工作就是发送消息、处理消息和移除消息的一个过程
- msg.target是在sendMessage发送消息的时候将当前handler进行赋值
- 在开启同步屏障的时候创建的Message对象里的msg.target对象为null
- 注意开启同步屏障后,记得在适当时机关闭屏障