概述
关于Android消息的处理机制,网上有太多的资料,之前也看过一些,但老实讲印象都不太深刻,尤其对这种理论性特别强的知识点,更是看得多也忘得多。今天我尝试按自己理解的方式来表述一下Android消息的处理机制,当然之前也是有查阅过很多资料的,话不多说,如下:
- Handle:
关于消息传递,通常我们接触最多应该就是Handle了,因为我们总是会在后台获取到消息后通过Handle来通知主线程(UI线程)来更新控件,最简单明了的一句代码就是handler.sendMessage(message),就能让主线程知道接下来我要干什么了。所以我们可以简单的把Handle当作两个线程之间通信的使者,因为使者通常会携带者信息(message)。 - Message:
Message很容易理解,就是我们需要传递的消息。 - Looper:
大家有没有想过主线程跟其他线程的区别,为什么主线程可以作为主线程呢(问的有点玄乎)。首先主线程一定是线程,这个毋庸置疑,当我们点击进入应用的时候,至少会有3个线程,2个Binder线程和一个主线程。我们知道线程也有生命周期的,通常执行完代码,线程就会终止,既然终止了,那我们的应用还怎么跑下去呢,所以我们需要在主线程加入死循环,让程序一直在运作,而Looper就起到了重要作用。主线程不停的循环来等待发来的消息(例如更新UI,Activity的启动等等),消息一进来,主线程就会做出相应的处理,这就是Looper的作用。所以主线程是个无限循环的线程,不断的循环遍历发送给它的消息,做出相应的处理。那么多条消息在主线程中是如何运作的呢?当没有消息的时候,主线程就会进入休眠状态,释放cpu的资源。再看MessageQueue。 - MessageQueue:
看到Queue就知道是队列,当主线程收到消息的时候,会把处在休眠状态中的主线程唤醒,消息存在于MessageQueue中的。一个线程不能同时处理多个消息,所以MessageQueue可以把这些消息以列表的形式一个个拿出来交给主线程做处理。
这里结合源码再分析一下。
源码分析
以前学习Java的时候我们知道程序的入口就是main()方法,那么在Android中,那Java程序入口在哪里呢?且看ActivityThread.class1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public final class ActivityThread {
public static final void main(String[] args) {
......
Looper.prepareMainLooper();//创建Looper对象
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
......
}
}
Looper.class: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
57public final class Looper {
//线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// ThreadLocal 的 set 方法,用来保证一个线程只有一个 Looper 对象,这样就保证了线程的安全
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Looper对象,并放入ThreadLocal<Looper> sThreadLocal静态变量中
sThreadLocal.set(new Looper(quitAllowed));
}
//调用 prepare(false),并然后给sMainLooper赋值。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//static 方法,方便获取主线程的Looper.
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
public static Looper myLooper() {
//简单理解相当于map.get(Thread.currentThread()) 获取当前线程的Looper
return sThreadLocal.get();
}
public static void loop() {
final Looper me = myLooper();
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 {
// 通过 target 去 dispatchMessage 而 target 就是绑定的 Handler
msg.target.dispatchMessage(msg);
} finally {
// 消息回收循环利用
msg.recycleUnchecked();
}
}
}
}
我们可以看到在prepareMainLooper() 创建了一个 Looper 对象,并放入ThreadLocal sThreadLocal静态变量中,而且保证一个线程只有一个 Looper对象,Looper.loop() 里面是一个死循环,不断的从 消息队列 MessageQueue 中取消息,然后通过 Handler 执行。
问题研究
主线程中的Looper.loop()一直无限循环为什么不会造成ANR?结合上面的分析能回答出来吗? - 在looper启动后,主线程上执行的任何代码都是被looper从消息队列里取出来执行的。也就是说主线程之后都是通过其他线程给它发消息来实现执行其他操作的。生命周期的回调也是如此的,系统服务ActivityManagerService通过Binder发送IPC调用给APP进程,App进程接到到调用后,通过App进程的Binder线程给主线程的消息队列插入一条消息来实现的。也就是说假如出现ANR的情况,那是因为当主线程给其他线程发消息后,其他线程未能及时处理执行,才导致的ANR。