一、Handler的使用可以查看我这篇文章
二、源码分析Handler消息处理的流程
我们要玩的东西也不是很难,我们通过源码来分析Handler如何发送消息到MessageQueue,然后Looper如何如何将消息从MessageQueue取出分发
给Handler。
(一)、创建主线程的MessageQueue和Looper对象
- 首先查看应用的主线程ActivityThread的main方法
public static void main(String[] args) {
// 创建一个Looper对象,并创建MessageQueue对象
Looper.prepareMainLooper();
// 开启消息轮询,轮询消息
Looper.loop();
}
- 查看**prepareMainLooper()**方法
public static void prepareMainLooper() {
// 最终还是调用了prepare()方法
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
- 查看**prepare()**方法
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建一个Looper对象并且存入ThreadLocal中
// 主线程的Looper对象
sThreadLocal.set(new Looper(quitAllowed));
}
此处我们可以查看下sThreadLocal的set()方法,其实就是以当前线程为key来保存数据
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- 查看Looper的构造方法new Looper(quitAllowed)
private Looper(boolean quitAllowed) {
// 在Looper的构造方法中创建了全局唯一的主线程的
// MessageQueue对象
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
至此,主线程的Looper对象和MessageQueue对象已经创建完成,可以用来处理消息了。
(2)、将消息发送到消息队列
- 我们就简单的看一下sendMessage方法,其他的传递消息的方法都是一样的流程
public final boolean sendMessage(@NonNull Message msg) {
// 其实调用的还是发送延迟消息的方法
return sendMessageDelayed(msg, 0);
}
- 继续查看sendMessageDelayed
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 最终是会调用到如下发送消息的方法
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
- 查看sendMessageAtTime方法
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// 这个mQueue哪里来的?后面讲解到Handler创建的源码的时候
// 我们就会看到,暂时挖个坑,O(∩_∩)O哈哈~
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 调用到enqueueMessage方法,并且将消息队列的对象
// queue传递到方法中
return enqueueMessage(queue, msg, uptimeMillis);
}
- 接下来我们查看enqueueMessage方法,最终我们可以看到此时是调用了MessageQueue的enqueueMessage方法将消息存入消息队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// Looper的loop方法中分发消息的Handler对象就是在此处赋值的
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 最终调用的是MessageQueue的消息入队方法
return queue.enqueueMessage(msg, uptimeMillis);
}
- 最终我们来到了MessageQueue的enqueueMessage方法中,将消息存入消息队列
mMessages = msg
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
// 最终看到此时是将传递进来的消息赋值给了全局的
// Message对象"mMessages"
mMessages = msg;
needWake = mBlocked;
} else {
......
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
(三)、从消息队列取消息
-
回到ActivityThread的main方法,我们看到在main方法中中调用了Looper的loop方法来轮询消息
-
查看Looper的loop方法
public static void loop() {
// 获取Looper对象,回头看上面在Looper的prepare方法中,
// 已经将Looper的对象保存在sThreadLocal
final Looper me = myLooper();
// 从Looper中取出消息队列MessageQueue对象,
// mQueue在Looper的构造方法中被创建
final MessageQueue queue = me.mQueue;
.......
// 一个死循环来取出消息
for (;;) {
// 将消息从消息队列中取出
Message msg = queue.next();
......
// 最终调用Handler的dispatchMessage方法将消息分发到Handler
// msg.target是在消息存入消息队列的时候赋的值,可以往前查看发送消息的源码,有标注
msg.target.dispatchMessage(msg);
.......
}
}
// myLooper方法其实就是从sThreadLocal中根据key(当前线程)
// 取出当前的Looper对象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- 查看MessageQueue的next方法
Message next() {
// 此方法获取到消息并且将消息返回
....
// 我们可以看到如下代码,全局的消息对象赋值给当前消息
// mMessages对象在保存消息的时候赋的值
Message msg = mMessages;
....
return msg;
}
- 最终我们继续回到Looper的loop方法中去查看,此时调用了Handler的
dispatchMessage方法将消息分发给Handler处理
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
// Handler的post方法就会走到如下的逻辑
// 有兴趣可以看下我上一篇讲解Handler的使用,可以看到
// 这里为什么是post方法的处理逻辑
handleCallback(msg);
} else {
if (mCallback != null) {
// mCallback就是我们创建Handler的时候传递的CallBack对象
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
(四)、Handler的使用
我们查看Handler的构造方法
public Handler(@Nullable Callback callback, boolean async) {
......
// 此处获取Looper对象,在Looper的prepare方法的时候已经存入
// ThreadLocal中
mLooper = Looper.myLooper();
// 如果取不到Looper对象就会抛出异常
// 这里也是为什么不能直接在子线程创建Handler的原因,在子线程
// 中取不到Looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// mQueue对象就是在Looper的构造方法中创建的
// 消息存入消息队列的MessageQueue对象就是在此处赋值的
// 这是全局唯一的MessageQueue对象
mQueue = mLooper.mQueue;
// 将Callback赋值给全局对象,消息的分发会用到
mCallback = callback;
mAsynchronous = async;
}
如果要在子线程中使用Handler对象,可以去查看HandlerThread的源码,
会教你怎么使用。O(∩_∩)O哈哈~
总的流程图
三、日常的一些问题
1、为什么主线程Looper使用死循环不会引起ANR异常?
因为Looper.loop()在开启死循环的时候,一旦需要等待时,或者还没有执行到需要执行的代码的时候,会调用NDK中的JNI方法释放当前时间片,
这样就不会引发ANR异常了。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
2、为什么Handler构造方法中的Looper不是直接new?
如果在Handler中直接new一个Looper,无法保证Looper的唯一,只有
Looper.prepare()才能保证Looper的唯一性。
private static void prepare(boolean quitAllowed) {
// 判断如果此时Looper已经存在就会直接抛出异常,保证Looper的唯一
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 直接使用私有的构造方法new一个Looper存入ThreadLocal,
// 根据当前线程作为key值取存储的
sThreadLocal.set(new Looper(quitAllowed));
}
// Looper的私有的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
3、MessageQueue为什么要放在Looper的私有构造方法中来初始化?
因为一个线程只能绑定一个Looper,所以在Looper的私有构造方法中初始化就可以保证MessageQueue也是唯一的线程对应一个Looper对应一个MessageQueue。