Android消息处理机制详解(含Native层)

Android应用程序的消息处理机制是围绕消息队列来实现的。
一个线程拥有一个消息队列之后,就可以进人到一个消息循环中,同时其他线程和该线程本身可以往这个消息队列发送消息,以便可以
在这个消息被处理时执行一个特定的操作。这样我们就将一个线程的生命周期划分为:

  1. 创建消息队列
  2. 消息循环:
    1. 发送消息
    2. 处理消息

Android系统主要通过MessageQueueLooperHandler三个类来实现Android应用程序的消息处理
机制,其中,MessageQueue是消息队列; Looper类用来创建消息队列,以及进人消息循环;
Handler类用来发送消息和处理消息。

接下来,我们首先分析线程消息队列的创建过程,然后分析线
程的消息循环过程,最后分析线程消息的发送和处理过程。

消息队列的创建过程

消息队列使用android.os.MessageQueue对象来描述的,它可以通过调用android.os.Looper
类的函数:prepareMainLooper()或者prepare()来创建

Looper类和MessageQueue

Android应用程序的消息处理机制不仅可以在Java代码中使用,也可以在C++代码中使用,
因此,Android系统在C++层中也有一个相应的Looper类和NativeMessageQueue类。其中,Java层中的
Looper类和MessageQueue类是通过C++层中的Looper类和NativeMessageQueue类来实现的。

Java层中的每一个Looper对象内部都有一个类型为MessageQueue的成员变量mQueue,它指向了一
MessageQueue对象; 而在C++层中,每一个NativeMessageQueue对象内部都有一个类型为Looper的成
员变量mLooper,它指向了一个C++层的Looper对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
private long mTraceTag;
......
}

每一个MessageQueue对象都有一个类型为int的成员变量mPtr,它保存了C++层中的一
NativeMessageQueue对象的地址值,这样我们就可以将Java层中的一个MessageQueue对象与C++层中
的一个NativeMessageQueue对象关联起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public final class MessageQueue {
private static final String TAG = "MessageQueue";
private static final boolean DEBUG = false;
// True if the message queue can be quit.
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
//保存C++层中的一个NativeMessageQueue对象的地址值
private long mPtr; // used by native code
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked;
// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
......
}

每一个MessageQueue对象还有一个类型为Message的成员变量mMessages,用来描述
一个消息队列(一个Message对象是链表的一个结点),我们可以调用MessageQueue类的成员函数enqueueMessage来往里面添加一个消息。

C++层中的Looper对象有两个类型为int的成员变量mWakeReadPipeFdmWakeWritePipeFd,
分别用来描述一个管道的读端文件描述符写端文件描述符当一个线程的消息队列没有消息需要处
理时,它就会在这个管道的读端文件描述符上进行睡眠等待,直到其他线程通过这个管道的写端文件
描述符来唤醒它为止。

当调用Java层的Looper类的prepareMainLooper或者prepare函数来为一个线程创
建一个消息队列时,Java层的Looper类就会在这个线程中创建一个Looper对象和一个MessageQueue
对象。在创建Java层的MessageQueue对象的过程中,又会调用它的成员函数nativeInit在C++层中创
建一个NativeMessageQueue对象和一个Looper对象。在创建C++层的Looper对象时,又会创建一个
管道,这个管道的读端文件描述符和写端文件描述符就保存在它的成员变量mWakeReadPipeFd
mWakeWritePipeFd中。

创建过程

Looper类有一个类型为ThreadLocal的静态成员变量sThreadLocal,它是用来保存线程中的Looper
对象的,每一个创建了消息队列的Android应用程序线程在里面都有一个关联的Looper对象。当我们
调用sThreadLocal.get()或者Looper.myLooper()时,就可以获得与当前线
程关联的一个Looper对象; 而当我们调sThreadLocal.set时,就可以将一个Looper对
象与当前线程关联起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Looper{
······
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static void prepare() {
prepare(true);
}
//为应用程序的其他子线程创建消息队列。
private static void prepare(boolean quitAllowed) {
//if语句检查当前线程是否已经有一个`Looper`对象了。如果有,就抛出一个异常;
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//否则,就会首先创建一个Looper对象,然后将这个Looper对象保存在Looper类的静态成员变量sThreadLocal 中。
sThreadLocal.set(new Looper(quitAllowed));
}
//用来为应用程序的主线程创建消息队列
public static void prepareMainLooper() {
//首先调用Looper类的静态成员函数prepare在当前线程中创建一个Looper对象
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//接着调用`Looper.setMainLooper`将这个Looper对象保存在Looper类的静态成员变量`mMainLooper`中
sMainLooper = myLooper();
}
}
······
}

Looper对象在创建的过程中,会创建一个MessageQueue对象,并且保存在它的成员变量mQuene中:

1
2
3
4
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

MessageQueue对象在创建的过程中,又会在C++层中创建一个NativeMessageQueue对象,通过调用MessageQueue类的成员函数nativeInit来实现的:

1
2
3
4
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}

nativeInit对应android_os_MessageQueue.cpp中的android_os_MessageQueue_nativeInit方法:

1
2
3
4
5
6
7
8
9
10
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
//首先在C++层中创建一个NativeMessageQueue对象
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (! nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return;
}
//调用函数android_os_MessageQueue_setNativMessQ将它与参数obj 所描述的一个Java层的MessageQueue对象关联起来。
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}

android_os_MessageQueue_setNativMssageeue的实现:

1
2
3
4
5
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
NativeMessageQueue* nativeMessageQueue) {
env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
reinterpret_cast<jint>(nativeMessageQueue));
}

gMessageQueueClassInfo是一个全局变量:

1
2
3
4
5
static struct {
jclass clazz; //指向Java层的MessageQueue类
jfieldID mPtr; // 指向Java层的MessageQueue类的成员变量mPtr
} gMessageQueueClassInfo;

NativeMessageQueue对象的创建:

1
2
3
4
5
6
7
NativeMessageQueue::NativeMessageQueue() {
mLooper = Looper::getForThread(); //获取当前线程的Native Looper对象
if (mLooper == NULL) { //为获取到则创建,被设置给当前线程
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}

Native Looper的创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks),
mResponseIndex(0) {
int wakeFds[2];
int result = pipe(wakeFds); //创建管道
mWakeReadPipeFd = wakeFds[0]; //保存读端文件描述符
mWakeWritePipeFd = wakeFds[1]; //保存写端文件描述符
//这个管道在一个线程的消息循环过程中起到的作用非常大。
//当一个线程没有新的消息需要处理时,它就会睡眠在这个管道的读端文件描述符上,直到有新的消息需要处理为止;
//当其他线程向这个线程的消息队列发送了一个消息之后,其他线程就会通过这个管道的写端文件描述符往这个管道
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
......
#ifdef LOOPER_USES_EPOLL
// Allocate the epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
//第调用函数epoll_create来创建一个epoll实例,并且将它的文件描述符保存在C++层的Looper类的成员变量mEpollFd中。
......
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
//将前面所创建的管道的读端文件描述符添加到这个epoll实例中,以便可以对它所描述的管道的写操作进行监听。
.......
#else
......
#endif
......
}
Looper::~Looper() {
close(mWakeReadPipeFd);
close(mWakeWritePipeFd);
}

Linux系统的epoll机制是为了同时监听多个文件描述符的IO读写事件而设计的,它是一个多路复用IO接口,类似于Linux系统的select机制,但是它是select机制的增强版。如果一个epoll实例监听了大量的文件描述符的IO读写事件,但是只有少量的文件描述符是活跃的,即只有少量的文件描述符会发生IO读写事件,那么这个epoll实例可以显著地减少CPU的使用率,从而提高系统的并发处理能力。

线程消息循环过程

消息队列创建完成之后,调用Java层的Looper.loop类的静态成员函数loop使它进入到一个消息中:

Looper.loop()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void loop() {
final Looper me = myLooper(); //首先获得当前线程的消息队列
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
......
//不断地检查这个消息队列中是否有新的消息需要处理。
for (;;) {
Message msg = queue.next(); // might block
//如果有新的消息需要处理,那么获得的Message对象msg就不等于null;
//否则,当前线程就会在MessageQueue类的成员函数next中进入睡眠等待状态,直到有新的消息需要处理为止。
if (msg == null) {
......
return;
}
}
}

MessageQuene.next

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//用来保存注册到消息队列中的空闲消息处理器(IdleHandler) 的个数,它们是在后面的代码中计算的。
//当线程发现它的消息队列没有新的消息需要处理时,不是马上就进入睡眠等待状态,而是先调用注册到它的消息队列中的IdleHandler对象的成员函数queueIdle,以便它们有机会在线程空闲时执行一些操作。
//这些IdleHandler对象的成员函数queueIdle是在后面的代码中调用的
int nextPollTimeoutMillis = 0;
//用来描述当消息队列中没有新的消息需要处理时,当前线程需要进人睡眠等待状态的时间。
//如果变量nextPollTimeoutMillis的值等于0,那么就表示即使消息队列中没有新的消息需要处理,当前线程也不要进入睡眠等待状态。
//如果变量nextPollTimeoutMlis的值等于-1,那么就表示当消息队列中没有新的消息需要处理时,当前线程需要无限地处于睡眠等待状态,直到它被其他线程唤醒为止。
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//for循环不断地调用成员函数nativePollOnce来检查当前线程的消息队列中是否有新的消息需要处理。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
//MessageQueue类内部有一个类型为Message的成员变量mMessages,用来描述当前线程需要处理的消息。
//当前线程从成员函数nativePollOnce返回来之后,如果它有新的消息需要处理,即MessageQueue类的成员变量mMessages不等于null,那么就会对它进行处理;
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
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.
//首先获得MessageQueue类的成员变量mMessages所描述的消息的处理时间。如果这个消息的处理时间小于等于系统的当前时间,那么就说明当前线程需要马上对它进行处理。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
//接下来代码就会将它返回给上一步处理,并且将MessageQueue类的成员变量mMessages重新指向下一个需要处理的消息;
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
//msg为空代码就会将变量nextPolTimeouMilis的值设置为-1。
//表示当前线程下次在调用成员函数nativePollOnce时,如果没有新的消息需要处理,那么就要无限地处于睡眠等待状态,直到它被其他线程唤醒为止。
nextPollTimeoutMillis = -1;
}
......
}
......
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}

在进入睡眠等待状态之前,当前线程会分发一个。线程空闲消息给那些已经注册到消息队列中的IdleHandler对象来处理,在注册到消息队列中的IdleHandler对象处理完成一个线程空闲消息之后执行的,它主要是将变量nextPollTimeoutMillis的值重新设置为0,表示当前线程接下来在调用成员函数nativePollOnce时,不可以进入睡眠等待状态。由于注册的IdleHandler对象在处理线程空闲消息期间,其他线程可能已经向当前线程的消息队列发送了一个或者若千个消息,因此,这时候当前线程就不能够在成员函数nativePollOnce中进入睡眠等待状态,而是要马上返回来检查它的消息队列中是否有新的消息需要处理。

MessageQueue.nativePollOnce

MessageQueue类的成员函数nativePollOnce是一个JNI方法,它是由C++层中的函数android_os_MessageQueue_nativePollc来实现的:

1
2
3
4
5
6
7
//参数obj指向了一个Java层的`MessageQueue`对象
//而参数`ptr`指向了这个`MessageQueue`对象的成员变量mPtr。
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}

从前面可以知道,MessageQueue类的成员变量mPtr保存的是一个C++层的NativeMessageQueu对象的地址值。因此,就可以安全地将参数pt转换成一个NativeMessageQueue对象,接着再调用这个NativeMessageQueu对象的成员函数pollOnce来检查当前线程是否有新的消息需要处理。

NativeMessageQueue.pollOnce

1
2
3
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}

mLooper是C++层的Looper对象

Looper.pollOnce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
//for循环不断地调用成员函数polInner来检查当前线程是否有新的消息需要处理。
for (;;) {
······
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = NULL;
if (outData != NULL) *outData = NULL;
return result;
}
//如果有新的消息需要处理,那么成员函数pllInner的返回值就不会等于0,这时候就会跳出for循环,以便当前线程可以对新的消息进行处理。
result = pollInner(timeoutMillis);
}
}

Looper.pollInner

之前我们已经为当前线程在C++层中创建了一个epoll实例,并且将它的文件描述符保存在C++层的Looper类的成员变量mEpollFd中,同时还将一个管道的读端文件描述符注册到它里面,以便可以监听这个管道的IO写事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int Looper::pollInner(int timeoutMillis) {
······
int result = ALOOPER_POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
······
#ifdef LOOPER_USES_EPOLL
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//调用函数epoll_wait来监听注册在前面所创建的epoll实例中的文件描述符的IO读写事件。
//如果这些文件描述符都没有发生IO读写事件,那么当前线程就会在函数epoll_wait中进人睡眠等待状态,等待的时间由最后一个参数timeoutMillis来指定
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
bool acquiredLock = false;
#else
······
#endif
······
#ifdef LOOPER_USES_EPOLL
//检查是哪一个文件描述符发生了IO读写事件。
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
//检查发生IO读写事件的文件描述符是否是与当前线程所关联的一个管道的读端文件描述符mWakeReadPipeFd。
//如果是,并且它所发生的IO读写事件的类型为EPOLLIN,那么这时候就说明其他线程向与当前线程所关联的一个管道写人了一个新的数据。
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
} else {
······
}
}
return result;
}

Looper.awoken

1
2
3
4
5
6
7
8
9
void Looper::awoken() {
······
char buffer[16];
ssize_t nRead;
do {
//将与当前线程所关联的一个管道的数据读出来。
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}

当前线程根本不关心写人到与它所关联的管道的数据是什么,它只是简单地将这些数据读取出来,以便可以清理这个管道中的旧数据。这样当前线程在下一次消息循环时,如果没有新的消息需要处理,那么它就可以通过监听这个管道的IO写事件进人睡眠等待状态,直到其他线程向它的消息队列发送了一个新的消息为止。假设当前线程没有新的消息需要处理,那么它就会调用函数epoll_wait进入睡眠等待状态,直到其他线程向它的消息队列发送了一个新的消息为止。

线程消息的发送过程

Android 提供一个android.os.Handler类,用来向一个线程的消息队列发送一个消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public final class Handler {
······
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
······
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if(delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(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(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}

Looper.sendMessage

sendMessage最终会调用sendMessageAtTime,到在成员函数sendMessageAtTime中,参数msg用来描述一个即将发送的消息,而参数uptimeMillis则用来描述这个即将发送的消息的处理时间。
首先将参数msg的成员变量target设置为当前正在处理的一个Handler对象,接着msg所描述的一个消息发送到一个消息队列中,这个消息队列是由当前正在处理的Handler对象的成员变量mQueue来描述的。

MessageQueue.enqueueMessage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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) {
······
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//首先将这个成员变量的值保存在变量needWake中,以便当前线程接下来可以决定是否需要将目标线程唤醒。
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.
//将要发送的消息插人到目标消息队列中之后,就检查变量needWake的值是否等于true。
//如果等于为true,那么接下来就会调用nativeWake将目标线程唤醒。
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

一个消息队列中的消息是按照它们的处理时间从小到大的顺序来排列的。因此,当我们将
个消息发送到一个消息队列时,需要先根据这个消息的处理时间找到它在目标消息队列的合适位
置,然后再将它插人到目标消息队列中。
将一个消息插人到一个目标消息队列中有四种情况:

  1. 目标消息队列是一个空队列。
  2. 插人的消息的处理时间等于0。
  3. 插人的消息的处理时间小于保存在目标消息队列头的消息的处理时间。
  4. 插人的消息的处理时间大于等于保存在目标消息队列头的消息的处理时间。

对于前面三种情况来说,要插人的消息都需要保存在目标消息队列的头部。对于最后一种情况来说,要插人的消息需要保存在目标消息队列中间的某一个位置上,通过for循环找到。如果要插入的消息的处理时间与目标消息队列中的某一个消息的处理时间相同,那么后来插
入的消息会被保存在后面,这样就可以保证先发送的消息可以先获得处理。

将一个消息插人到一个目标消息队列之后,可能需要将目标线程唤醒,有两种情况:

  1. 插人的消息在目标消息队列中间
  2. 插人的消息在目标消息队列头部。

对于第一种情况来说,由于保存在目标消息队列头部的消息没有发生变化,因此,当前线程不需要对目标线程执行唤醒操作,这时候就会将变量needWake的值设置为false。对于第二种情况来说,由于保存在目标消息队列头部的消息发生了变化,因此,当前线程就需要将目标线程唤醒,以便它可以对保存在目标消息队列头部的新消息进行处理。但是,如果这时候目标线程不是正处于睡眠等待状态,那么当前线程就不需要对它执行唤醒操作。当前正在处理的MessageQueue对象的成员变量mBlocked记录了目标线程是否正处于睡眠等待状态。如果它的值等于
true,那么就表示目标线程正处于睡眠等待状态,这时候当前线程就需要将它唤醒。

MessageQueue.nativeWake

1
2
3
4
5
6
7
8
/**
* @param obj Java层的MessageQueue对象
* @param ptr obj指向的Java层的MessageQueue对象的成员变量mPtr,保存着与之关联的NativeMessageQueue对象的地址值
*/
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
return nativeMessageQueue->wake();
}

NativeMessageQueue.wake

1
2
3
void NativeMessageQueue::wake() {
mLooper->wake(); //调用C++层Looper对的的wake函数
}

Looper.wake

1
2
3
4
5
6
7
8
9
10
11
12
13
void Looper::wake() {
······
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1) {
if (errno != EAGAIN) {
LOGW("Could not write wake signal, errno=%d", errno);
}
}
}

mWakeWritePipeFd是用来描述一个管道的写端文件描述符的。write函数向它写人一个“W”字符,即向它所描述的管
道写人一个新的数据,这时候目标线程就会因为这个管道发生了一个IO写事件而被唤醒。

线程消息处理过程

从前面可以知道,当一个线程没有新的消息需要处理时,它就会在C++层的Looper类的成员函数polInner中进入睡眠等待状态,因此,当这个线程有新的消息需要处理时,它首先会在C++层的Looper类的成员函数polInner中被唤醒,然后沿着之前的调用路径一直返回到Java层的
Looper类的静态成员函数loop中,最后就可以对新的消息进行处理了。

Looper.loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
······
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
······
try {
//msg.target指向的是一个Handler对象
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
······
}

Handler.dispatchMessage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void dispatchMessage(Message msg) {
//如果要处理的消息在发送时指定了一个回调接口,就会调用Handler类的成员函数handleCallback来处理这个消息。
if (msg.callback != null) {
//如果负责分发消息的Handler对象的成员变量mCallback指向了一个回调接口,就会调用这个回调接口mCallback的成员函数handleMessage来处理这个消息。
handleCallback(msg);
} else {
if (mCallback != null) {
//负责分发消息的Handler对象的成员变量mCallback所指向的一个回调接口希望这个消息可以继续向下处理
//就会调用Handler类的成员函数handleMessage来处理这个消息。
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}

Handler.handleMessage

Handle类的成员函数handleMessage是一个空实现。一般情况下,我们不直接使用Handler 类来发
送消息,而是使用它的一个子类来发送消息,这个子类重写了父类Handler的成员函数handleMessage
这样我们就可以创建这个子类的一个对象来发送消息,并且将这个消息交给这个子类对象的成员函数handleMessage来处理

空闲消息的处理过程

有一种特殊的消息,它们不用事先发送到一个消息队列中,而是由一个线程在空闲的时候主动发送出来。这种特殊的线程消息称
为线程空闲消息。
线程空闲消息是由一种称为空闲消息处理器的对象来处理的,这些空闲消息处理器必须要实现ldlelHandler接口。

1
2
3
public static interface IdleHandler {
boolean queueIdle();
}

一个空闲消息处理器如果想要接收到一个线程的空闲消息,那么它就必须要注册到这个线程的消息队列中,即注册到与这个线程所关联的一个MessageQueue对象中。MessageQueue类有一个成员变量mIdleHandlers,它指向了一个IdleHandler列表,用来保存一个线
程的空闲消息处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}

一个线程在消息循环的过程中,有时候会变得“无事可做”。当一个线程的消息队列为空,或者保存在消息队列头部的消息的处理时间大于系统的当前时间时,就会发生这种情况。这时候线程就处于一种空闲状态,接下来它就会进人睡眠等待状态。不过,
在进入睡眠等待状态之前,这个线程会发出一个线程空闲消息给那些注册了的空闲消息处理器来处理。

Message.next:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Message next() {
······
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
······
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
······
//首先检查变量pendingldleHandlerou的值是否小于0。
//如果小于0,那么获得所有注册到当前线程的消息队列中的空闲消息处理器的个数,并且保存在变量pendingldleHandlerCou中。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//再检查变量pendingldleHandlero的值是否<=0。
//只有在两种情况下,变量pendingldleHandlerCou的值才会<=0,其中,第一种情况是没有空闲消息处理器注册到当前线程的消息队列中; 第二种情况是之前已经发送过一个线程空闲消息了。
//如果变量pendingldleHandlerCou的值<=0,那么当前线程就不会再发出一个线程空闲消息了。
//,在MessageQueue类的成员函数next的 一次调用 中,一个线程最多只会发出一个线程空闲消息。
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//首先将注册到当前线程的消息队列中的空闲消息处理器拷贝到一个空闲消息处理器数组mPendingldleHanders中
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 再依次调用这个数组中的每一个空闲消息处理器的成员函数queueldle来接收一个线程空闲消息。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
extPollTimeoutMillis = 0;
}
}

参考

参考:《Android系统源代码情景分析》