1.角色
陆续出场 Handler、Message、MessageQueue、Looper
2.开场白(Handler是什么)
其实只要弄明白了上面第二个问题,自然就会知道handler从发送消息到接收消息更新UI这个过程中发生了什么。不过为了理解更彻底一些,最好是先了解一下handler是什么?用来解决什么问题。
看一下官网对于Handler的描述:
A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper’s message queue and execute them on that Looper’s thread.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
这段话的大概意思是,handler是一个与线程和消息队列绑定的消息处理程序,可以通过它来发送和处理消息。有两种主要用途:(1)调度消息和运行项,以便在未来的某个时间点执行;(2)将要在不同线程上执行的操作加入消息队列。
上面提到的两个主要用途,可能理解起来比较困难,我们来看一个简单的例子:
比如现在有一个下载操作,要求下载完成之后Toast提示一下 下载成功。这里有两个动作,一个是下载,一个是Toast提示,下载是耗时操作,而Toast是更新UI。一般情况下,Android更新UI的操作是在主线程进行的,子线程是不能直接更新UI的,但是耗时操作如果放到主线程的话是会引发ANR阻塞的,所以可以把下载放到子线程,Toast提示放到主线程,那么主线程的Toast如何知道下载操作已经完成了呢?换句话说,子线程和主线程如何通信呢?使用Handler处理类似情况。
3.先发一条消息(Handler、Message出场)
刚刚提到的下载并Toast的例子用handler实现:
Handler handler = new Handler(Looper.myLooper(), msg -> {
Toast.makeText(MainActivity.this,msg.obj.toString(),Toast.LENGTH_LONG).show();
return false;
});
btn_click_b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.obj = "下载完成";
handler.sendMessage(message);
}).start();
}
});
在上面例子中,我们先让子线程阻塞了3s,然后构建了一条消息,调用handler.sendMessage()把构建的消息发送出去,发送到什么地方去了呢?看一下sendMessage(msg)方法的内部是怎么做的。
//handler.sendMessage(msg)的调用链
//handler.sendMessage->sendMessageDelayed->sendMessageAtTime->enqueueMessage->queue.enqueueMessage
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 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) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
4.消息发送到哪里去了(MessageQueue出场)
跟踪整个调用过程发现sendMessage方法经过各种曲折调用之后,最后调用到了MessageQueue类的enqueueMessage方法,其他send方法,包括post方法最后也都会调用到这个enqueueMessage方法。流程进行到这一步之后,核心处理逻辑就已经不在Handler.java这个类中了,而是转移到了MessageQueue类中。接下来看一下MessageQueue.enqueueMessage类的内部:
Message mMessages;
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.");
}
synchronized (this) {
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;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//消息列表里没有数据
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//死循环,不断向消息链表添加数据
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Message 内部有一个其自身类型的变量next,所以Message可以理解成一个专门存放消息的链表结构。 上面代码的核心逻辑就是,通过一个死循环将收到的信息不断添加到这个链表中去。
5. 只进不出?(Looper出场)
整个过程走到这里,Handler处理机制中的两个关键类Handler、MessageQueue已经陆续登场了。Handler、MessageQueue完成了发送消息和消息入队的过程,我们的消息总不能只发送不接收吧,那么谁来负责取出消息呢?取出消息之后又是怎么处理的呢?轮到Looper上场了!
我们都知道Handler机制中Looper.loope()方法的作用就是从消息列表中不断获取消息,具体是怎么获取呢?看一下loop()方法的内部:
for (;;) {
Message msg = queue.next(); // 调用MessageQueue.next从Message队列中取出消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
msg.target.dispatchMessage(msg);//取出消息之后向下分发
}
整个方法的全部代码比较长,没必要理解全部的逻辑,只需关注for循环就行,这个死循环里其实就做了两件事情,1 不断从Message队列取消息 2 每取出一条消息都将其分发出去。取消息的操作是在MessageQueue.next方法中进行的,核心代码如下:
for (;;) {
......
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
}
......
}
取消息的核心操作都是在一个死循环中进行的,逐条从Message队列中轮询获取每个Message对象然后将其返回给Looper。
6.兜兜转转回到原点
到这一步Looper就已经取出来了,那么接下来Looper又是怎么处理取到的消息对象的呢?它把消息又分发出去了!。在前面Looper.loope()方法的源码中有代码是msg.target.dispatchMessage(msg),通过字面意思就可以知道,这是一个分发消息的操作,消息分发到哪里去了呢?继续看dispatchMessage(msg)的内部:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
是不是很熟悉?!dispatchMessage这个方法是Handler内部的一个方法,也就是说,我们通过Handler发出的消息兜兜转转又回到了Handler!接下来的事情大家都很清楚了,我们在Activity中调用了handleMessage回调,拿到了当初发送出去的消息,然后就开始处理其他逻辑了。至此,演出结束,众演员退场。