Android消息机制

在Android系统中,主要有两种通信机制:Binder机制和消息机制。Binder机制用于跨进程通信,消息机制用于进程内部通信,也就是同一个进程中内部线程之间的通信。

消息机制概述

通常在工作线程中做完一些操作后需要更新UI,但是,UI控件不是线程安全的,不能直接在工作线程中更新UI。这时,工作线程需要通过UI线程的Handler对象向UI线程发送消息,通知UI线程去更新UI。这个过程的实质就是线程之间通过消息机制进行通信。

Android消息机制主要通过Handler实现,而Handler的运行需要MessageQueue和Looper配合。

MessageQueue是一个消息队列,采用链式队列的数据结构实现,用来存储Handler发送的消息。

Looper可以理解为消息循环,每个要处理消息的线程都拥有一个属于自己的Looper,这个Looper会以无限循环的方式查询MessageQueue,如果有消息,就通过消息对应的Handler对象处理消息,否则,就一直等待。

Handler有两个作用:一方面可以让其它线程通过Handler给拥有这个Handler对象的线程发送消息;另一方面可以在线程的Looper获取消息后,通过Handler处理这个消息。

需要注意的是,线程默认是没有Looper的,如果要使用Handler,必须先为线程创建一个Looper,并且,一个线程只能有一个Looper。不过,一个线程可以拥有多个Handler对象,这个并不影响Looper处理消息,因为不管是哪个Handler发送过来的消息,都一并存在MessageQueue,Looper只会串行获取这些消息进行处理。

MessageQueue分析

MessageQueue是一个链式队列,当消息过来时,会将消息对象Message插入到队列中;当要处理消息时,会读取队列中的消息,并删除已读取的消息。

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
/**
* frameworks/base/core/java/android/os/MessageQueue.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public final class MessageQueue {
...
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息队列为空,插入的消息处于队首,如果消息队列被阻塞,需要将其唤醒。
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;
prev.next = msg;
}
...
}
return true;
}
...
}

MessageQueue通过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
/**
* frameworks/base/core/java/android/os/MessageQueue.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public final class MessageQueue {
...
Message next() {
...
int nextPollTimeoutMillis = 0;
for (;;) { // 无线循环。
...
synchronized (this) {
// 检索下一个Message,如果存在就返回这个Message。
final long now = SystemClock.uptimeMillis();
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) {
// 下一个消息没有准备好,为其设置一个唤醒时间。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取到下一个消息。
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 {
// 没有更多消息了。
nextPollTimeoutMillis = -1;
}
...
}
}
...
}

Looper分析

Looper在消息机制中实现消息循环,具体来说,它会不停地从MessageQueue中查看是否有新消息,如果有就进行处理,没有就一直循环等待。

一个线程默认是没有Looper的,要让一个普通线程升级为Looper线程需要做一下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程。
Looper.prepare();

// 其他处理,如实例化handler。
...

// 开始循环处理消息队列。
Looper.loop();
}
}

这个过程并没有涉及到MessageQueue,这是因为Looper将MessageQueue进行了封装,开发者不需要直接与MessageQueue交互,这点可以从Looper的构造函数中看出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* frameworks/base/core/java/android/os/Looper.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public final class Looper {
...
private Looper(boolean quitAllowed) {
// Looper内的消息队列。
mQueue = new MessageQueue(quitAllowed);
// 当前线程。
mThread = Thread.currentThread();
}
...
}

Looper.prepare()

prepare()方法用来为线程添加Looper对象,并且一个Thread只能有一个Looper对象,具体可以看其源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* frameworks/base/core/java/android/os/Looper.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public final class Looper {
...
// sThreadLocal.get()会返回null除非调用了prepare()方法。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常。
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
...
}

可以看出,prepare()方法是通过ThreadLocal保证了每个线程只能有一个Looper对象。这里简单介绍下ThreadLocal的使用原理。

ThreadLocal的使用原理

ThreadLocal是一个创建线程局部变量的类。通常情况下,创建的变量是可以被任何一个线程访问并修改的,而使用ThreadLocal创建的变量,每个线程访问时都会获取其一个副本,这个副本只能在当前线程访问,其他线程无法访问和修改。ThreadLocal从本质上讲,是提供了一个“线程级”的变量作用域,它是一种线程封闭(每个线程独享变量)技术,更直白点讲,ThreadLocal可以理解为将对象的作用范围限制在一个线程上下文中,使得变量的作用域为“线程级”。

注意

除了prepare()方法,Looper还提供了prepareMainLooper()方法专门为主线程(ActivityThread,也即UI线程)提供Looper对象,其本质也是通过prepare()方法创建Looper对象。不过,由于主线程的Looper对象比较特殊,Looper提供了一个getMainLooper()方法,通过这个方法可以在其它线程也能获取到主线程的Looper对象,所以,主线程的Looper对象不是ThreadLocal类型的。另外,Android系统在创建主线程的时候就已经为其初始化了Looper对象,所以,一般不需要另外调用prepareMainLooper()方法。

Looper.loop()

loop()方法用来开启消息循环,只有调用了这个方法,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
/**
* frameworks/base/core/java/android/os/Looper.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public final class Looper {
...
public static void loop() {
final Looper me = myLooper(); // 得到当前线程Looper。
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 得到当前Looper的MessageQueue。
...
for (;;) { // 开启无线循环。
Message msg = queue.next(); // 获取Message,可能会被阻塞。
if (msg == null) {
// 没有消息需要处理,退出无线循环。
return;
}
...
try {
// msg.target即发送这个消息的Handler对象,用这个Handler对象来处理消息。
msg.target.dispatchMessage(msg);
} finally {
...
}
...
msg.recycleUnchecked(); // 回收消息资源。
}
}
...
}

可以看出,loop()方法是一个无线循环,不断从MessageQueue中获取消息并进行处理,唯一跳出循环的方式MessageQueue的next()方法返回了null。

loop()方法使用了myLooper()方法,其源码如下:

1
2
3
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

由于ThreadLocal的原因,在任意线程调用Looper.myLooper()方法返回的都是当前线程的Looper对象。

退出消息循环

消息循环是可以退出的,Looper提供了quit()和quitSafely()方法来退出消息循环,这两个方法的区别是:quit()方法会直接退出消息循环;而quitSafely()方法只是设定了一个退出标记,只有当把消息队列中已有的消息处理完毕后才退出消息循环,这种方式比较安全。

如果在一个线程中创建Looper开启了消息循环,那么在任务完成后,需要主动退出消息循环,否则,该线程会一直处于等待状态,不会被终止。

Handler分析

Handler用来向Looper的消息队列发送消息,在Looper获取这个消息后,会调用该Handler处理这个消息,这个过程是异步的。

创建Handler

由于Handler需要向Looper的消息队列发送消息,所以,在初始化Handler时,需要关联一个Looper对象。但是,在一个线程中,通常直接使用Handler的无参构造函数去创建Handler对象,并没有直接让Handler对象与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
/**
* frameworks/base/core/java/android/os/Handler.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public class Handler {
...
final Looper mLooper; // 关联的Looper。
final MessageQueue mQueue; // 关联的MessageQueue。
final Callback mCallback;
final boolean mAsynchronous;
...
public Handler() {
this(null, false);
}
...
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); // 默认将关联当前线程的Looper。
if (mLooper == null) {
// Looper不能为空,即该默认的构造方法只能在拥有Looper的线程中使用。
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
...
}

所以,可以通过以下代码为在Looper线程中创建Handler对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class LooperThread extends Thread {
private Handler handler1;
private Handler handler2;

@Override
public void run() {
// 将当前线程初始化为Looper线程。
Looper.prepare();

// 实例化两个Handler。
handler1 = new Handler();
handler2 = new Handler();

// 开始循环处理消息队列。
Looper.loop();
}
}

需要说明的是,一个Looper线程能拥有多个Handler对象。加入多个Handler对象后,LooperThread的结构图如下所示:

LooperThread的结构

发送消息

Handler创建完毕后,就可以使用post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long)和sendMessageDelayed(Message, long)等方法发送消息了。

通过这些API的参数看以看到,Handler不但可以发送Message对象,还能发送Runable对象,其实,Runable对象在底层也会被封装成Message对象发送给对应的消息队列。下面源码展示以post(Runnable)方法发送消息的过程:

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
/**
* frameworks/base/core/java/android/os/Handler.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public class Handler {
...
public final boolean post(Runnable r)
{
// getPostMessage(r)将Runnable封装成Message。
return sendMessageDelayed(getPostMessage(r), 0);
}
...
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r; // 将Runnable设为Message的callback。
return m;
}
...
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) {
// Message的target必须设为当前Handler,因为Looper对象会用这个Handler对象处理此消息。
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); // 将消息插入到消息队列。
}
...
}

处理消息

发送消息后,在Looper.loop()方法中会调用msg.target.dispatchMessage(msg)方法来处理消息,这个target就发送消息的Handler对象。以下源码展示了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
/**
* frameworks/base/core/java/android/os/Handler.java
* 开源项目: Android 版本名称: Nougat MR1 API Level: 25
*/

public class Handler {
...
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果Message设置了callback,即Runnable消息,处理callback。
handleCallback(msg);
} else {
// 如果Handler本身设置了callback,则执行callback。
if (mCallback != null) {
// 这种方式运行使用Handler handler = new Handler(callback)方式创建Handler;
// 避免通过继承Handler重写handleMessage()方法。
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果Message没有callback,则调用Handler的handleMessage()方法。
handleMessage(msg);
}
}
...
private static void handleCallback(Message message) {
// 处理Runnable消息。
message.callback.run();
}
...
// 使用Handler handler = new Handler(callback)方式创建Handler所使用的Callback接口。
public interface Callback {
public boolean handleMessage(Message msg);
}
...
// 由子类实现的处理消息的方法,默认实现为空。
public void handleMessage(Message msg) {
}
...
}

消息机制通信的流程

经过以上分析,消息机制的原理已经很清楚了,下面以一幅图来展示消息机制通信的整个流程:

消息机制通信的流程

坚持原创技术分享,您的支持将鼓励我继续创作!