Android中的异步消息处理主要是由4个部分组成,Message、handler、MessageQueue和Looper。其中Message和Handler将会后续出文章详细介绍。本文重点介绍这四个部分。
Message
是线程之间传递的消息,它可以在内部携带少量的信息,用于在不同的线程之间交换数据信息,可以利用Message的what字段
,另外也可以使用arg1和arg2来携带一些整形数据
,使用obj字段携带一个Object对象。
给出参考代码:
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@SuppressLint("SetTextI18n")
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
text.setText("Nice to meet you mac");
break;
case UPDATE_TEXT2:
text.setText("Hello mac Wust!");
break;
default:
break;
}
}
};
顾名思义,Handler就是处理器的意思。主要用来发送和处理消息,发送消息使用Handler的sendMessage方法,发出的消息经过一系列处理之后,最终传递给Handler的handleMessage
方法。
给出参考代码:
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@SuppressLint("SetTextI18n")
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
text.setText("Nice to meet you mac");
break;
case UPDATE_TEXT2:
text.setText("Hello mac Wust!");
break;
default:
break;
}
}
};
@SuppressLint("NonConstantResourceId")
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_text:
new Thread(() -> {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}).start();
break;
case R.id.change_text2:
new Thread(() -> {
Message message = new Message();
message.what = UPDATE_TEXT2;
handler.sendMessage(message);
}).start();
break;
default:
break;
}
}
MessageQueue就是消息队列的意思。用于存放所有的通过Handler发送的消息,这部分的消息会一直存在于消息队列中,等待被处理。每一个线程中只有MessageQueue对象。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
根据上述代码,可以得到消息被处理的时间 = 当前时间+延迟的时间。
差异在于使用SystemClock.uptimeMillis(),而不用SystemClock. currentTimeMillis()。
Handler.sendMessageDelayed()方法最终会调用enqueueMessage方法
进入MessageQueue的enqueueMessage方法中,源码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
其中最为重要的就是两个方法:
给出enqueueMessage源码:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
//msg.target就是发送此消息的Handler
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;//将延迟时间封装到msg内部
Message p = mMessages;//消息队列的第一个元素
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,则此消息需要被立即处理,此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延时的消息,则将其添加到队列中,原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程
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;
}
}
// invariant: p == prev.next
msg.next = p;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
//唤醒线程
nativeWake(mPtr);
}
}
return true;
}
MessageQueue中enqueueMessage方法
的目的有两个:
下面给出next方法源码:
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
return null;
}
// -1 only during first iteration
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//正常取出消息
//设置mBlocked = false代表目前没有阻塞
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
//没有消息,会一直阻塞,直到被唤醒
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
根据源码可以得知,
消息的入列和出列是一个生产-消费者模式,Looper.loop()在一个线程中调用next()不断的取出消息,另外一个线程则通过enqueueMessage向队列中插入消息,所以在这两个方法中使用了synchronized (this) {} 同步机制,其中this为MessageQueue对象
,不管在哪个线程,这个对象都是同一个,因为Handler中的mQueue指向的是Looper中的mQueue
,这样防止了多个线程对同一个队列的同时操作。
Looper类分别用来对一个线程开启一个消息循环。
默认情况下Android中新诞生的线程是没有开启消息循环的,主线程除外,主线程系统会自动给她创建looper,并且开启消息循环。
Looper对象通过MessageQueue消息存放消息和事件,一个线程只有一个Looper,对应一个MessageQueue。
Handler可以看作Looper的一个接口,用来指定向Looper发送消息以及定义处理的方法。
具体请见gitee地址:请见master分支的AndroidThreadTest模组。