- 用数组?不太合适,数组虽然在遍历的时候会比较快,但需要预先就申请固定的内存空间,导致在插入数据和移除数据时可能需要移动大量数据。而 MessageQueue 可能随时会收到数量不定、时间戳大小不定的 Message,消费完 Message 后还需要将该其移出队列,所以使用数组并不合适
- 用链表?好像可以,链表在插入数据和移除数据时只需要改变指针的引用即可,不需要移动数据,内存空间也只需要按需申请即可。虽然链表在随机访问的时候性能不高,但是对于 MessageQueue 而言无所谓,因为在消费 Message 的时候也只需要取队列头的值,并不需要随机访问
好了,既然决定用链表结构,那么 Message 就需要增加一个字段用于指向下一条消息才行
/**
* @Author: leavesC
* @Date: 2020/12/1 13:31
* @Desc:
* GitHub:https://github.com/leavesC
*/
public final class Message {
//唯一标识
public int what;
//数据
public Object obj;
//时间戳
public long when;
//下一个节点
public Message next;
}
MessageQueue 需要提供一个 enqueueMessage 方法用来向链表插入 Message,由于存在多个线程同时向队列发送消息的可能,所以方法内部还需要做下线程同步才行
/**
* @Author: leavesC
* @Date: 2020/12/1 13:31
* @Desc:
* GitHub:https://github.com/leavesC
*/
public class MessageQueue {
//链表中的第一条消息
private Message mMessages;
void enqueueMessage(Message msg, long when) {
synchronized (this) {
Message p = mMessages;
//如果链表是空的,或者处于队头的消息的时间戳比 msg 要大,则将 msg 作为链表头部
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
} else {
Message prev;
//从链表头向链表尾遍历,寻找链表中第一条时间戳比 msg 大的消息,将 msg 插到该消息的前面
for (; ; ) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
}
}
}
此外,MessageQueue 要有一个可以获取队头消息的方法才行,就叫做next() 吧。外部有可能会随时向 MessageQueue 发送 Message,next() 方法内部就直接来开启一个无限循环来反复取值吧。如果当前队头的消息可以直接处理的话(即消息的时间戳小于或等于当前时间),那么就直接返回队头消息。而如果队头消息的时间戳比当前时间还要大(即队头消息是一个延时消息),那么就计算当前时间和队头消息的时间戳的差值,计算 next() 方法需要阻塞等待的时间,调用 nativePollOnce() 方法来等待一段时间后再继续循环遍历
//用来标记 next() 方法是否正处于阻塞等待的状态
private boolean mBlocked = false;
Message next() {
int nextPollTimeoutMillis = 0;
for (; ; ) {
nativePollOnce(nextPollTimeoutMillis);
synchronized (this) {
//当前时间
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
//如果当前时间还未到达消息的的处理时间,那么就计算还需要等待的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//可以处理队头的消息了,第二条消息成为队头
mMessages = msg.next;
msg.next = null;
mBlocked = false;
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
mBlocked = true;
}
}
}
//将 next 方法的调用线程休眠指定时间
private void nativePollOnce(long nextPollTimeoutMillis) {
}
此时就需要考虑到一种情形:当 next() 还处于阻塞状态的时候,外部向消息队列插入了一个可以立即处理或者是阻塞等待时间比较短的 Message。此时就需要唤醒休眠的线程,因此 enqueueMessage 还需要再改动下,增加判断是否需要唤醒next() 方法的逻辑
void enqueueMessage(Message msg, long when) {
synchronized (this) {
//用于标记是否需要唤醒 next 方法
boolean needWake = false;
Message p = mMessages;
//如果链表是空的,或者处于队头的消息的时间戳比 msg 要大,则将 msg 作为链表头部
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
//需要唤醒
needWake = mBlocked;
} else {
Message prev;
//从链表头向链表尾遍历,寻找链表中第一条时间戳比 msg 大的消息,将 msg 插到该消息的前面
for (; ; ) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
//唤醒 next() 方法
nativeWake();
}
}
}
//唤醒 next() 方法
private void nativeWake() {
}
3、Handler
既然存放消息的地方已经确定就是 MessageQueue 了,那么自然就还需要有一个类可以用来向 MessageQueue 发送消息了,就叫它 Handler 吧。Handler 可以实现哪些功能呢?
- 希望除了可以发送 Message 类型的消息外还可以发送 Runnable 类型的消息。这个简单,Handler 内部将 Runnable 包装为 Message 即可
- 希望可以发送延时消息,以此来执行延时任务。这个也简单,用 Message 内部的 when 字段来标识希望任务执行时的时间戳即可
- 希望可以实现线程切换,即从子线程发送的 Message 可以在主线程被执行,反过来也一样。这个也不难,子线程可以向一个特定的 mainMessageQueue 发送消息,然后让主线程负责循环从该队列中取消息并执行即可,这样不就实现了线程切换了吗?
所以说,Message 的定义和发送是由 Handler 来完成的,但 Message 的分发则可以交由其他线程来完成
根据以上需求:Runnable 要能够包装为 Message 类型,Message 的处理逻辑要交由 Handler 来定义,所以 Message 就还需要增加两个字段才行
/**
* @Author: leavesC
* @Date: 2020/12/1 13:31
* @Desc:
* GitHub:https://github.com/leavesC
*/
public final class Message {
//唯一标识
public int what;
//数据
public Object obj;
//时间戳
public long when;
//下一个节点
public Message next;
//用于将 Runnable 包装为 Message
public Runnable callback;
//指向 Message 的发送者,同时也是 Message 的最终处理者
public Handler target;
}
Handler 至少需要包含几个方法:用于发送 Message 和 Runnable 的方法、用来处理消息的 handleMessage 方法、用于分发消息的 dispatchMessage 方法
/**
* @Author: leavesC
* @Date: 2020/12/1 13:31
* @Desc:
* GitHub:https://github.com/leavesC
*/
public class Handler {
private MessageQueue mQueue;
public Handler(MessageQueue mQueue) {
this.mQueue = mQueue;
}
public final void post(Runnable r) {
sendMessageDelayed(getPostMessage(r), 0);
}
public final void postDelayed(Runnable r, long delayMillis) {
sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final void sendMessage(Message r) {
sendMessageDelayed(r, 0);
}
public final void sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public void sendMessageAtTime(Message msg, long uptimeMillis) {
msg.target = this;
mQueue.enqueueMessage(msg, uptimeMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = new Message();
m.callback = r;
return m;
}
//由外部来重写该方法,以此来消费 Message
public void handleMessage(Message msg) {
}
//用于分发消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
msg.callback.run();
} else {
handleMessage(msg);
}
}
}
之后,子线程就可以像这样来使用 Handler 了:将子线程持有的 Handler 对象和主线程关联的 mainMessageQueue 绑定在一起,主线程负责循环从 mainMessageQueue 取出 Message 后再来调用 Handler 的 dispatchMessage 方法,以此实现线程切换的目的
Handler handler = new Handler(mainThreadMessageQueue) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1: {
String ob = (String) msg.obj;
break;
}
case 2: {
List<String> ob = (List<String>) msg.obj;
break;
}
}
}
};
Message messageA = new Message();
messageA.what = 1;
messageA.obj = "https://github.com/leavesC";
Message messageB = new Message();
messageB.what = 2;
messageB.obj = new ArrayList<String>();
handler.sendMessage(messageA);
handler.sendMessage(messageB);
4、Looper
现在就再来想想怎么让 Handler 拿到和主线程关联的 MessageQueue,以及主线程怎么从 MessageQueue 获取 Message 并回调 Handler。这之间就一定需要一个中转器,就叫它 Looper 吧。Looper 具体需要实现什么功能呢?
- 每个 Looper 对象应该都是对应一个独有的 MessageQueue 实例和 Thread 实例,这样子线程和主线程才可以互相发送 Message 交由对方线程处理
- Looper 内部需要开启一个无限循环,其关联的线程就负责从 MessageQueue 循环获取 Message 进行处理
- 因为主线程较为特殊,所以和主线程关联的 Looper 对象要能够被子线程直接获取到,可以考虑将其作为静态变量存着
这样,Looper 的大体框架就出来了。通过 ThreadLocal 来为不同的线程单独维护一个 Looper 实例,每个线程通过 prepare() 方法来初始化本线程独有的 Looper 实例 ,再通过 myLooper() 方法来获取和当前线程关联的 Looper 对象,和主线程关联的 sMainLooper 作为静态变量存在,方便子线程获取
/**
* @Author: leavesC
* @Date: 2020/12/1 13:31
* @Desc:
* GitHub:https://github.com/leavesC
*/
final class Looper {
final MessageQueue mQueue;
final Thread mThread;
private static Looper sMainLooper;
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private Looper() {
mQueue = new MessageQueue();
mThread = Thread.currentThread();
}
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static void prepareMainLooper() {
prepare();
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
public static Looper myLooper() {
return sThreadLocal.get();
}
}
Looper 还需要有一个用于循环从 MessageQueue 获取消息并处理的方法,就叫它loop() 吧。其作用也能简单,就是循环从 MessageQueue 中取出 Message,然后将 Message 再反过来分发给 Handler 即可
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();//可能会阻塞
msg.target.dispatchMessage(msg);
}
}
这样,主线程就先通过调用prepareMainLooper() 来完成 sMainLooper 的初始化,然后调用loop() 开始向 mainMessageQueue 循环取值并进行处理,没有消息的话主线程就暂时休眠着。子线程拿到 sMainLooper 后就以此来初始化 Handler,这样子线程向 Handler 发送的消息就都会被存到 mainMessageQueue 中,最终在主线程被消费掉
5、做一个总结
这样一步步走下来后,读者对于 Message、MessageQueue、Handler、Looper 这四个类的定位就应该都很清晰了吧?不同线程之间就可以依靠拿到对方的 Looper 来实现消息的跨线程处理了
例如,对于以下代码,即使 Handler 是在 otherThread 中进行初始化,但 handleMessage 方法最终是会在 mainThread 被调用执行的,
Thread mainThread = new Thread() {
@Override
public void run() {
//初始化 mainLooper
Looper.prepareMainLooper();
//开启循环
Looper.loop();
}
};
Thread otherThread = new Thread() {
@Override
public void run() {
Looper mainLooper = Looper.getMainLooper();
Handler handler = new Handler(mainLooper.mQueue) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1: {
String ob = (String) msg.obj;
break;
}
case 2: {
List<String> ob = (List<String>) msg.obj;
break;
}
}
}
};
Message messageA = new Message();
messageA.what = 1;
messageA.obj = "https://github.com/leavesC";
Message messageB = new Message();
messageB.what = 2;
messageB.obj = new ArrayList<String>();
handler.sendMessage(messageA);
handler.sendMessage(messageB);
}
};
再来做个简单的总结:
- Message:用来表示要执行的任务
- Handler:子线程持有的 Handler 如果绑定到的是主线程的 MessageQueue 的话,那么子线程发送的 Message 就可以由主线程来消费,以此来实现线程切换,执行 UI 更新操作等目的
- MessageQueue:即消息队列,通过 Handler 发送的消息并非都是立即执行的,需要先按照 Message 的优先级高低(延时时间的长短)保存到 MessageQueue 中,之后再来依次执行
- Looper:Looper 用于从 MessageQueue 中循环获取 Message 并将之传递给消息处理者(即消息发送者 Handler 本身)来进行消费,每条 Message 都有个 target 变量用来指向 Handler,以此把 Message 和其处理者关联起来。不同线程之间通过互相拿到对方的 Looper 对象,以此来实现跨线程发送消息
有了以上的认知基础后,下面就来看看实际的源码实现 ~ ~
二、Handler 源码
1、Handler 如何初始化
Handler 的构造函数一共有七个,除去两个已经废弃的和三个隐藏的,实际上开发者可以使用的只有两个。而不管是使用哪个构造函数,最终的目的都是为了完成 mLooper、mQueue、mCallback、mAsynchronous 这四个常量的初始化,同时也可以看出来 MessageQueue 是由 Looper 来完成初始化的,而且 Handler 对于 Looper 和 MessageQueue 都是一对一的关系,一旦初始化后就不可改变
大部分开发者使用的应该都是 Handler 的无参构造函数,而在 Android 11 中 Handler 的无参构造函数已经被标记为废弃的了。Google 官方更推荐的做法是通过显式传入 Looper 对象来完成初始化,而非隐式使用当前线程关联的 Looper
Handler 对于 Looper 和 MessageQueue 都是一对一的关系,但是 Looper 和 MessageQueue 对于 Handler 可以是一对多的关系,这个后面会讲到
@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;
//省略其它构造函数
/**
* @hide
*/
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2、Looper 如何初始化
在初始化 Handler 时,如果外部调用的构造函数没有传入 Looper,那就会调用Looper.myLooper() 来获取和当前线程关联的 Looper 对象,再从 Looper 中取 MessageQueue。如果获取到的 Looper 对象为 null 就会抛出异常。根据异常信息 Can't create handler inside thread that has not called Looper.prepare() 可以看出来,在初始化 Handler 之前需要先调用 Looper.prepare() 完成 Looper 的初始化
走进 Looper 类中可以看到,myLooper() 方法是 Looper 类的静态方法,其只是单纯地从 sThreadLocal 变量中取值并返回而已。sThreadLocal 又是通过 prepare(boolean) 方法来进行初始化赋值的,且只能赋值一次,重复调用将抛出异常
我们知道,ThreadLocal 的特性就是可以为不同的线程分别维护单独的一个变量实例,所以,不同的线程就会分别对应着不同的 Looper 对象,是一一对应的关系
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//只允许赋值一次
//如果重复赋值则抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
此外,Looper 类的构造函数也是私有的,会初始化两个常量值:mQueue 和 mThread,这说明了 Looper 对于 MessageQueue 和 Thread 都是一一对应的关系,关联之后不能改变
@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在日常开发中,我们在通过 Handler 来执行 UI 刷新操作时,经常使用的是 Handler 的无参构造函数,那么此时肯定就是使用了和主线程关联的 Looper 对象,对应 Looper 类中的静态变量 sMainLooper
@UnsupportedAppUsage
private static Looper sMainLooper; // guarded by Looper.class
//被标记为废弃的原因是因为 sMainLooper 会交由 Android 系统自动来完成初始化,外部不应该主动来初始化
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
prepareMainLooper() 就用于为主线程初始化 Looper 对象,该方法又是由 ActivityThread 类的 main() 方法来调用的。该 main() 方法即 Java 程序的运行起始点,所以当应用启动时系统就自动为我们在主线程做好了 mainLooper 的初始化,而且已经调用了Looper.loop() 方法开启了消息的循环处理,应用在使用过程中的各种交互逻辑(例如:屏幕的触摸事件、列表的滑动等)就都是在这个循环里完成分发的
正是因为 Android 系统已经自动完成了主线程 Looper 的初始化,所以我们在主线程中才可以直接使用 Handler 的无参构造函数来完成 UI 相关事件的处理
public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
···
Looper.prepareMainLooper();
···
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
3、Handler 发送消息
Handler 用于发送消息的方法非常多,有十几个,其中大部分最终调用到的都是 sendMessageAtTime() 方法。uptimeMillis 即 Message 具体要执行的时间戳,如果该时间戳比当前时间大,那么就意味着要执行的是延迟任务。如果为 mQueue 为 null,就会打印异常信息并直接返回,因为 Message 是需要交由 MessageQueue 来处理的
public boolean sendMessageAtTime(@NonNull 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);
}
需要注意 msg.target = this 这句代码,target 指向了发送消息的主体,即 Handler 对象本身,即由 Handler 对象发给 MessageQueue 的消息最后还是要交由 Handler 对象本身来处理
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将消息交由 MessageQueue 处理
return queue.enqueueMessage(msg, uptimeMillis);
}
4、MessageQueue
MessageQueue 通过 enqueueMessage 方法来接收消息
- 因为存在多个线程同时往一个 MessageQueue 发送消息的可能,所以
enqueueMessage 内部肯定需要进行线程同步 - 可以看出 MessageQueue 内部是以链表的结构来存储 Message 的(Message.next),根据 Message 的时间戳大小来决定其在消息队列中的位置
- mMessages 代表的是消息队列中的第一条消息。如果 mMessages 为空(消息队列是空的),或者 mMessages 的时间戳要比新消息的时间戳大,则将新消息插入到消息队列的头部;如果 mMessages 不为空,则寻找消息列队中第一条触发时间比新消息晚的非空消息,将新消息插到该消息的前面
到此,一个按照时间戳大小进行排序的消息队列就完成了,后边要做的就是从消息队列中依次取出消息进行处理了
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
···
msg.markInUse();
msg.when = when;
Message p = mMessages;
//用于标记是否需要唤醒线程
boolean needWake;
//如果链表是空的,或者处于队头的消息的时间戳比 msg 要大,则将 msg 作为链表头部
//when == 0 说明 Handler 调用的是 sendMessageAtFrontOfQueue 方法,直接将 msg 插到队列头部
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果当前线程处于休眠状态 + 队头消息是屏障消息 + msg 是异步消息
//那么就需要唤醒线程
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//从链表头向链表尾遍历,寻找链表中第一条时间戳比 msg 大的消息,将 msg 插到该消息的前面
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
//如果在 msg 之前队列中还有异步消息那么就不需要主动唤醒
//因为已经设定唤醒时间了
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
知道了 Message 是如何保存的了,再来看下 MessageQueue 是如何取出 Message 并回调给 Handler 的。在 MessageQueue 中读取消息的操作对应的是next() 方法。next() 方法内部开启了一个无限循环,如果消息队列中没有消息或者是队头消息还没到可以处理的时间,该方法就会导致 Loop 线程休眠挂起,直到条件满足后再重新遍历消息
@UnsupportedAppUsage
Message next() {
···
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//将 Loop 线程休眠挂起
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
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.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
···
}
···
}
···
}
}
next() 方法又是通过 Looper 类的 loop() 方法来循环调用的,loop() 方法内也是一个无限循环,唯一跳出循环的条件就是 queue.next() 方法返回为 null。因为 next() 方法可能会触发阻塞操作,所以没有消息需要处理时也会导致 loop() 方法被阻塞着,而当 MessageQueue 有了新的消息,Looper 就会及时地处理这条消息并调用 msg.target.dispatchMessage(msg) 方法将消息回传给 Handler 进行处理
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
···
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
···
msg.target.dispatchMessage(msg);
···
}
}
Handler 的dispatchMessage 方法就是在向外部分发 Message 了。至此,Message 的整个分发流程就结束了
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
5、消息屏障
Android 系统为了保证某些高优先级的 Message(异步消息) 能够被尽快执行,采用了一种消息屏障(Barrier)机制。其大致流程是:先发送一个屏障消息到 MessageQueue 中,当 MessageQueue 遍历到该屏障消息时,就会判断当前队列中是否存在异步消息,有的话则先跳过同步消息(开发者主动发送的都属于同步消息),优先执行异步消息。这种机制就会使得在异步消息被执行完之前,同步消息都不会得到处理
Handler 的构造函数中的async 参数就用于控制发送的 Message 是否属于异步消息
public class Handler {
final boolean mAsynchronous;
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mAsynchronous = async;
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
//设为异步消息
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
MessageQueue 在取队头消息的时候,如果判断到队头消息就是屏障消息的话,那么就会向后遍历找到第一条异步消息优先进行处理
@UnsupportedAppUsage
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) { //target 为 null 即属于屏障消息
// Stalled by a barrier. Find the next asynchronous message in the queue.
//循环遍历,找到屏障消息后面的第一条异步消息进行处理
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
}
}
}
6、退出 Looper 循环
Looper 类本身做了方法限制,除了主线程外,子线程关联的 MessageQueue 都支持退出 Loop 循环,即 quitAllowed 只有主线程才能是 false
public final class Looper {
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
}
MessageQueue 支持两种方式来退出 Loop:
|