Android 消息机制

Android 消息机制

记忆点:

消息机制 -> Handler更新UI -> 主要相关类:

  1. Message:需要传递的信息,可以传递数据
  2. MessageQueue:消息队列,但不是队列,内部实现为一个单链表的数据结构,因为单链表在插入和删除上比较有优势。主要功能:向消息池投递消息MessageQueue.enqueueMessage和取走消息池的消息MessageQueue.next
  3. Handler:消息辅助类,主要功能:向消息池发送各种消息事件handler.sendMessage和处理响应消息事件handler.handleMessage
  4. Looper:不断循环执行Looper.loop,从 MessageQueue 中读取消息,按分发机制将消息分发给目标处者

-> 运行流程:

在子线程执行完耗时操作,当Handler发送消息时,将会调用MessageQueue.enqueueMessage,向消息队列中添加消息。当通过Looper.loop开启循环后,会不断从线程池中读取消息,即调用MessageQueue.next,然后调用目标 Handler (即发送该消息的 Handler)的dispatchMessage方法传递消息,然后返回到 Handler 所在线程,目标 Handler 收到消息,调用handleMessage方法,接收消息,处理消息。

运行流程

-> MessageQueue,Handler 和 Looper 三者之间的关系:

一个线程一个 Looper,可以有多个 Handler,Looper 中维护一个 MessageQueue 来维护消息队列,消息队列中的 Message 可以来自不同 Handler。

消息机制整体架构图

消息机制源码解析:

Looper

初始化:default:prepare(true),布尔值表示的是这个 Looper 是否可以退出。还限制不能重复创建 Looper。

开启 Looper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void loop(){
final Looper me = myLooper(); //获取TLS存储的Looper对象
if(me == null){
throw new RuntimeExceprion("No Looper; Looper.prepare() wasn't called on this thread");
}
final MesssageQueue queue = me.mQueue; //Looper中对象中的消息队列
...
for(;;){
Message msg = queue.next(); //可能会阻塞,因为next()方法可能会无限循环
... //判空、日志处理
msg.target.dispatchMessage(msg); // msg.target:目标Handler
...
msg.recycleUnchecked();
}
}

创建 Handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Handler(){
this(null, false);
}

public Handler(Callback callback, boolean async){
...

//若在主线程,系统已经帮我们 Looper.prepareMainLooper(); Looper.loop();并且Looper是不可退出的;其他线程需调用Looper.prepare()
mLooper = Looper.myLooper();
if(mLooper == null){
throw new RuntimeExceprion("");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

发送消息:

Handler的sendpost方式最终都是调用了sendMessageAtTime()。内部大致流程:sendMessageAtTime(msg, uptimeMillis)->enqueueMessage(queue, msg, uptimeMillis)->queue.enqueueMessage(msg, uptimeMillis)其中 queue 是以 mQueue 赋值的局部变量,而 mQueue 是消息队列,是从 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
// MessageQueue enqueueMessage() 方法的具体逻辑
boolean enqueueMessage(Message msg, long when){
...// 判空、msg是否已被使用
synchronized(this){
if(mQuitting){ //正在退出时,回收msg,加入到消息池
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if(p == null || when == 0 || when < p.when){
//p为null(代表MessageQueue没有消息)或者msg的触发时间是队列中最早的
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}else{
// 一般地,不需要唤醒事件队列,除非消息队头存在barrier,并且同时Message是队列中最早的异步消息
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for(;;){
// 将消息按触发时间顺序插入到MessageQueue
prev = p;
p = p.next;
if(p == null || when < p.when){
break;
}
if(needWake && p.isAsynchronous()){
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if(needWake){
nativeWake(mPtr);
}
}
return true;
}

获取消息

发送消息之后,在 MessageQueue 维护了消息队列,然后再 Looper 中通过loop()不断地获取消息。loop()->queue.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
Messsage next(){
final long ptr = mPtr;
if(ptr == 0){ //当消息循环已经退出,则直接返回
return null;
}
int pendingIdleHandlerCount = -1; //循环迭代的首次为-1
int nextPollTimeoutMillis = 0;
for(;;){
if(nextPollTimeoutMillis != 0){
Binder.flushPendingConmmands();
}
//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this){
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; // mMessages保存链表的第一个元素
if(msg != null && msg.target == null){
//当消息Handler为null时,查询MessageQueue中的下一条异步消息msg,为空则退出循环
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;
//设置消息的使用状态,即flages |= FLAG_IN_USE
msg.markInUse();
return msg;
}
}else {
nextPollTimeoutMillis = -1;
}
if(mQuitting){
dispose();
return null;
}
}
...
}
}

分发消息

loop()方法中,获取到下一条消息后,执行msg.target.dispatchMessage(msg),来分发消息到目标 Handler。

1
2
3
4
5
6
7
8
9
10
11
12
13
public void dispatchMessage(Message msg){
//消息分发的优先级
if(msg.callback != null){
handleCallback(msg);
}else {
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg); //很多情况下分发消息的处理方法为这个
}
}

图解

消息机制

其他知识

  1. ThreaLocal:每并不是一个 Thread,而是 Thread 的局部变量。当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中 Local 所要表达的意思
  2. Android 中主线程为什么不会被 Looper.loop() 死循环卡死?——保证程序一直执行,thread.attach(false);创建 Binder 通道(新线程,接收 AMS 发送来的事件);承载 ActivityThread 的主线程就是由 Zygote fork 而创建的进程;不耗 CPU 资源,涉及到 pipe/epoll 机制;Activity 的内部类 H 继承于 Handler,Activity 的生命周期都是依靠主线程的 Looper.loop,当收到不同的 Message 时采取相应的措施。

参考引用

Android消息机制的原理及源码解析

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

Android Handler 消息机制(解惑篇)