这是一个系列文章,总共三篇分别讲:
Handler(一):基本原理和知识点
Handler(二):Java层源码分析
Handler(三):Native层源码分析
一. 前言
通过上一篇文章,我们总结了Handler的设计模型,Handler的运行原理以及一些常见的知识点,本文要对Handler的源码进行深入分析。
后文源码基于Android API 35.
接下来我们结合App主线程启动(下图)来讲解,从App启动后一步一步往下走分析整个Handler机制源码。
然后按照以下顺序一边捋顺Handler的使用,一边分析源码。
- 异步通信准备
- 消息发送
- 消息循环
- 消息处理
二. 源码分析
1. 异步通信准备
在线程中开启Looper有两种:一种是系统源码中在主线程ActivityThread默认开启Looper,一种是子线程默认不启动,需手动开启,其实差别不大,我们分别看下源码。 在子线程中使用,可以使用系统自带 HandlerThread
。
1.1 ActivityThread.main方法
首先在ActivityThread类有我们熟悉的main的函数,App启动的代码的入口就在这里。
arduino
public final class ActivityThread {
public static final void main(String[] args) {
...
// 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
Looper.prepareMainLooper();
...
// 创建主线程
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
// 自动开启 消息循环
Looper.loop();
}
}
可以看出,调用prepareMainLooper对Looper进行初始化,调用loop来开启消息循环处理。
1.2 Looper.prepare方法
java
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
//prepare不能重复调用
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//一个线程只能有一个Looper,prepare不能重复调用
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
}
public final class MessageQueue {
MessageQueue(boolean quitAllowed) {
//mQuitAllowed 是否允许退出
mQuitAllowed = quitAllowed;
//natice层创建MessageQueue
mPtr = nativeInit();
}
}
在这里prepareMainLooper
方法只有主线程才能调用,其它线程应该调用prepare
。prepareMainLooper
中也是调用了prepare
,同时给sMainLooper
赋值,给sMainLooper
赋值是为了方便通过Looper.getMainLooper()
快速获取主线程的Looper。
我们看到prepare
做的事件就是new了一个Looper
实例并放入Looper
类下面一个static
的ThreadLocal<Looper> sThreadLocal
静态变量中。
看下Looper
的构造函数,创建了MessageQueue
,给当前线程创建了消息队列MessageQueue
,并且让**Looper
持有MessageQueue
的引用**。主线程从普通线程转成一个Looper线程。
其中MessageQueue.nativeInit
方法是在native层创建MessageQueue
,native层相关代码我们在下一篇分析。
补充:hread.attach(false);
会创建一个Binder线程(具体是指ApplicationThread
,该Binder线程会通过想 Handler
将Message
发送给主线程,之后讲)。我们之前提到主线程最后会进入无限循环当中,如果没有在进入死循环之前创建其他线程,那么待会谁会给主线程发消息呢?,没错就是在这里创建了这个线程,这个线程会接收来自系统服务发送来的一些事件封装了Message
并发送给主线程,主线程在无限循环中从队列里拿到这些消息并处理这些消息。(Binder线程发生的消息包括LAUNCH_ACTIVITY
,PAUSE_ACTIVITY
等等)
1.3 Looper.loop方法
然后看Looper.loop
方法
java
public final class Looper {
public static void loop() {
// 获取当前线程的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
Binder.clearCallingIdentity();
//确保在权限检查时基于本地进程,而不是调用进程。
final long ident = Binder.clearCallingIdentity();
me.mSlowDeliveryDetected = false;
// 这里进入了for循环,如果loopOnce返回false,则会退出循环,
// 也就表示MessageQueue不工作了,除非再次调用loop方法,
// 当调用quit方法后,loopOnce将会返回false
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
//没有消息,则退出循环,线程结束,程序也就退出了
return;
}
}
}
}
可以看到UI线程(Looper线程)开启了一个无线循环,在循环中通过loopOnce
方法从mQueue取出消息msg ,并调用msg .target(是一个handler)去执行,但是如果msg==null
则会退出循环,从而退出线程,这是为什么呢? 答案就在mQueue.next()
方法中,next拿消息时如果没用数据就会阻塞,挂起线程,等有消息放入消息队列后,唤醒线程返回消息给handler处理,这个重点方法我们待会再看。
至此,我们已经准备好一个线程以及绑定的Looper,和MessageQueue 。接下来我们看如何发送消息。
2. 消息发送
下面将依次分析Message, Handler,MessageQueue的源码,来了解消息是如何发送的。
2.1 Message
ini
public final class Message implements Parcelable {
public long when; // 执行时间(系统计算)
Handler target; //执行消息的handler
Runnable callback; //回调
Message next; //下一个消息
public static final Object sPoolSync = new Object();
private static Message sPool; //缓存池-第一个消息
private static int sPoolSize = 0; //缓存池-当前数量
private static final int MAX_POOL_SIZE = 50; //缓存池最大数量限制
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--; //缓存池数量减少
return m;
}
}
//缓存池没有就只能创建新的
return new Message();
}
// 回收message到缓存池
public void recycle() {
//判断消息是否正在使用
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
//将消息标示位置为IN_USE,并清空消息所有的参数。
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
// 缓存池数量未达上限,则放入缓存池
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++; //缓存池的可用大小进行加1操作
}
}
}
}
静态变量sPool
的数据类型为Message,通过next成员变量,维护一个消息池链表;静态变量MAX_POOL_SIZE
代表消息池的可用大小;消息池的默认大小为50。
消息池常用的操作方法是obtain(
)和recycle()
。使用obtain()创建消息对象,避免每次都使用new重新分配内存。使用recycle()回收处理过的消息对象放入缓存池中以待复用。
2.2 Handler
大部分开发者使用的应该都是 Handler 的无参构造函数,而在 Android 11 中 Handler 的无参构造函数已经被标记为废弃的了。Google 官方更推荐的做法是通过显式传入 Looper 对象来完成初始化,而非隐式使用当前线程关联的 Looper.
less
@Deprecated
public Handler() {
this(null, false);
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
@hide
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
this(looper, callback, async, /* shared= */ false);
}
/** @hide */
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
boolean shared) {
mLooper = looper;
mQueue = looper.mQueue;
//全局回调
mCallback = callback;
//异步标识,设置后handler的全部消息都变为异步消息
mAsynchronous = async;
//共享标识
mIsShared = shared;
}
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
可见Handler的构造中,主要就是传入looper
和MessageQueue
,以及执行消息时用的callback
。
2.3 Handler.sendXXX和Handler.postXXX方法
Handler 用于发送消息的方法非常多,
可以看到很多
send
开头的方法和post
开头的方法, 除了 sendMessageAtFrontOfQueue()
和postAtFrontOfQueue
方法之外,其他方法最终都会调用到 sendMessageAtTime()
, 以 post
开头的方法,发送的内容为 Runnable 对象,最终也会调用到 send
开头方法,发送 message。我们看几个常用的。
java
public class Handler {
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//传入任务
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 这里注意 传入的是msg具体要执行的时间戳
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
postDelayed
与 sendMessage
方法的区别是postDelayed
传入了一个可执行任务Runnable r
存到msg.callback
中,当Looper.loop
方法从消息队列取数数据执行时调用。
Handler 用于发送消息的方法大部分最终调用到的都是 sendMessageAtTime
方法。
2.4 Handle.sendMessageAtTime 方法
sendMessageAtFrontOfQueue()
和postAtFrontOfQueue
方法虽然不调用sendMessageAtTime
,但是和sendMessageAtTime
一样最终都调用enqueueMessage
方法。
less
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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//这里将其赋值为当前Handler对象,后续消息出队后可交给Handler处理消息,同时也为内存泄漏埋下伏笔。
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用消息队列入队
return queue.enqueueMessage(msg, uptimeMillis);
}
uptimeMillis 即 Message 具体要执行的时间戳,如果使用sendMessageDelayed
发送消息,uptimeMillis 取的是距离启动时间的毫秒数+延时时间。如果为 mQueue 为 null,就会打印异常信息并直接返回,最后调用queue.enqueueMessage
入队。
需要注意 msg.target = this
这句代码,target 指向了发送消息的主体,即 Handler 对象本身,即由 Handler 对象发给 MessageQueue 的消息最后还是要交由 Handler 对象本身来处理。
再看sendMessageAtFrontOfQueue()
和postAtFrontOfQueue
方法。
less
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 这里注意 传入的是msg具体要执行的时间戳
return enqueueMessage(queue, msg, 0);
}
sendMessageAtFrontOfQueue
方法传入的uptimeMillis
是0,会放入消息队列的最前面。
2.5 MessageQueue.enqueueMessage方法
我们分析到了全部的消息都会通过enqueueMessage
方法放入消息队列。
ini
public final class MessageQueue {
boolean enqueueMessage(Message msg, long when) {
//同步屏障消息 msg.target就是null,在这会不会报错呢? 肯定不会,因为同步屏障消息不从这个方法入队。
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//收到多个线程发的消息,线程同步
synchronized (this) {
...
msg.markInUse();
msg.when = when;
//mMessages是消息链表的头
Message p = mMessages;
//用于标记是否需要唤醒线程
boolean needWake;
/** 当消息链表头是空,当when == 0时,意味着这条消息是通过
sendMessageAtFrontOfQueue方法设置的,需要插在消息链表最前面
当when < p.when时,说明当前消息的执行时间更早,要放前面
*/
// 这里的三个条件满足其一都会将当前Message插入队头
// p == null,说明当前队列为空,新来的Message自然作为队头
// when = 0,说明此Message没有延时需要立即执行,意味着这条消息是通过sendMessageAtFrontOfQueue方法设置的所以也放在队头
// when < p.when,说明当前Message的延时小于队头Message的延时,所以放在队头
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
// 这里的mBlocked表示MessageQueue的出队函数next()是否阻塞。
// 如果是阻塞状态,则队头Message变化时要将其唤醒,表明这个时候
// 可能有消息需要出队处理了,特别是当when == 0 的时候。
needWake = mBlocked;
if (p == null) {
mLast = mMessages;
}
} else {
// 如果队头是一个同步屏障且当前入队的消息是异步消息,且mBlocked为true,则
// 需要唤醒当前线程来。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
// 若启用尾部跟踪(mLast 指向队列尾部),直接操作尾部指针提升插入效率
if (Flags.messageQueueTailTracking()) {
if (when >= mLast.when) {
needWake = needWake && mAsyncMessageCount == 0;
msg.next = null;
mLast.next = msg;
mLast = msg;
} else {
// 下面的操作是真正入队的操作,从队头开始遍历
Message prev;
for (;;) {
prev = p;
p = p.next;
// p == null 表示遍历到了队尾
// when < p.when 表示队列按延时时间排序
if (p == null || when < p.when) {
break;
}
// 若存在异步消息,可能抑制唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
if (p == null) {
mLast = msg;
}
// 将当前msg插入队列,插入p的前面
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
} else {
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;
mLast = null;
}
}
if (msg.isAsynchronous()) {
// 异步消息(如 ViewRootImpl 的绘制任务)可绕过同步屏障(Sync Barrier),优先执行
mAsyncMessageCount++; //异步消息计数
}
// needWake == true,表示目标线程的 Looper 处于阻塞状态(mBlocked == true),需要唤醒以处理新消息。
if (needWake) {
nativeWake(mPtr); // 调用 Native 层唤醒
}
}
return true;
}
}
可以看到,mMessages是消息链表的头,链表按Message 的延时执行时间参数 when
进行排序。
enqueueMessage
方法负责将消息按照时间顺序正确地插入到单链表结构的队列 中。根据不同的情况,选择将消息插入队头或者插入到队列中,也设计到一些关于同步屏障的处理,最终通过needWake
判断当前是否需要唤醒线程,nativeWake()
是一个native方法,我们同样放到后边分析。
2.6 MessageQueue.postSyncBarrier方法
MessageQueue
中的消息除了通过enqueueMessage
方法传入,还有一个postSyncBarrier
方法添加屏障消息,代码如下:
ini
public final class MessageQueue {
@hide
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
//不需要唤醒队列,因为同步屏障的目的是让它停滞不前。
synchronized (this) { //保证同步
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//没有赋值 msg.target,默认为null
Message prev = null;
Message p = mMessages;
//按顺序插入队列
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
}
可以看出postSyncBarrier
方法是向消息队列添加了一条同步屏障消息,并没立即唤醒线程执行。
至此我们已经分析了Handler中数据发送部分,以及Message如何进入到队列中的原理和源码。下面我们再分析,消息是如何取出来的。
3. 消息循环
3.1 Looper.loopOnce方法
在异步通信准备那小节,我们分析到Looper.loop
中开启了无限循环一直调用loopOnce
方法,接下来我们就分析一下loopOnce
:
java
public final class Looper {
public static void loop() {
...
// 这里进入了for循环,如果loopOnce返回false,则会退出循环,
// 也就表示MessageQueue不工作了,除非再次调用loop方法,
// 当调用quit方法后,loopOnce将会返回false
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
//没有消息,则退出循环,线程结束,程序也就退出了
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 执行MessageQueue的next()函数,返回下一个将要处理的Message,
// 如果下一个Message时间未到,next()方法则会阻塞等待。
// 这个函数下面会详细分析。
Message msg = me.mQueue.next(); // might block
if (msg == null) {
//没有消息,则返回
return false;
}
//消息执行前的输出,可以用于监控Handler中Message的执行耗时,避免发送ANR以及卡顿
//默认为null,可通过setMessageLogging()方法来指定输出
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
// sObserver可以通过Looper的setObserver方法设置,是用来监听回调
// Message的处理状态的。
final Observer observer = sObserver;
...
try {
// 派发消息到对应的Handler
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
//消息执行后的输出,可以用于监控Handler中Message的执行耗时,避免发送ANR以及卡顿
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//恢复调用者信息
final long newIdent = Binder.clearCallingIdentity();
//回收Message,重新放入缓存池
msg.recycleUnchecked();
return true;
}
}
可以看到UI线程(Looper线程)开启了一个无线循环,在循环中通过loopOnce
方法从mQueue取出消息msg ,并调用msg .target(是一个handler)去执行,但是如果msg==null
则会退出循环,从而退出线程,这是为什么呢? 答案就在mQueue.next()方法中,next
拿消息时如果没用数据就会阻塞,挂起线程,等有消息放入消息队列后,唤醒线程返回消息给handler处理,我们看这个重点方法next
。
3.2 MessageQueue.next方法
ini
public final class MessageQueue {
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
// nextPollTimeoutMillis 表示nativePollOnce方法需要等待nextPollTimeoutMillis才会返回
int nextPollTimeoutMillis = 0;
// 进入死循环,有合适的消息就返回,无消息就阻塞
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 调用Native方法读取消息,队里里没有消息有可能会堵塞, 阻塞时释放CPU资源
// 两种情况该方法才会返回(代码才能往下执行)
// 一种是等到有消息产生就会返回,
// 另一种是当等了nextPollTimeoutMillis时长后,nativePollOnce也会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
// nativePollOnce 返回之后才能往下执行
synchronized (this) {
// 获取下一个消息
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) { // 下一条消息还没准备好,设置超时以在准备就绪时唤醒。
// 虽然有消息,但是还没有到运行的时候,像我们经常用的postDelay,
// 计算出离执行时间还有多久赋值给nextPollTimeoutMillis,
// 表示nativePollOnce方法要等待nextPollTimeoutMillis时长后返回
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取到消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
if (prevMsg.next == null) {
mLast = prevMsg;
}
} else {
mMessages = msg.next;
if (msg.next == null) {
mLast = null;
}
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
if (msg.isAsynchronous()) {
mAsyncMessageCount--;
}
// 成功地获取MessageQueue中的下一条即将要执行的消息
return msg;
}
} else {
// 没有消息,nextPollTimeoutMillis复位
nextPollTimeoutMillis = -1;
}
// 消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
// 当消息队列为空,或者是消息队列的第一个消息时,取空闲handler(idleHandler)数。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 没有idle handlers 需要运行,则循环并等待。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 去掉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);
}
}
}
// 重置idle handler个数为0,以保证不会再次重复运行
pendingIdleHandlerCount = 0;
// 在调用空闲处理程序时,可能已传递新消息,因此请返回并再次查找待处理消息,而无需等待。
nextPollTimeoutMillis = 0;
}
}
}
next()
方法为出队方法。如果当前 MessageQueue 中存在 mMessages (即待处理消息),就将这个消息出队,然后让下一条消息成为 mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。如果有同步屏障的话,就忽略同步消息,一直读取直到读到异步消息并返回。而且如果有idleHandler时,并且当前是空闲(没有消息需要执行或者有消息没到执行时间)时,执行一次。
nativePollOnce
方法也是native方法,后边我们在分析。
至此我们已经分析完MessageQueue消息队列是如何循环的,在线程的Looper.loop()启动之后开启循环,不停的从消息队列(按消息执行时间排序的链表)中取消息,如果有消息并且when小于当前时间(表示可以执行),则返回并执行。 如果有消息但是when大于等于当前时间(表示未到执行时间),则挂起等到when时间执行。如果当前没有消息则执行idleHandler,然后挂起等待。
下面 我们再分析一下,从消息队列中取出的数据是如何处理。
4. 消息处理
在Looper的loopOnce方法中,取出message后传入了msg.target.dispatchMessage(msg)方法。
typescript
public class Handler {
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
}
当Looper.loop
方法从消息队列取数数据后,优先判断msg.callback != null
,然后判断全局mCallback != null
,最终走默认处理handleMessage
。
消息分发的优先级:
- Message的回调方法:
message.callback.run()
,优先级最高; - Handler的回调方法:
Handler.mCallback.handleMessage(msg)
,优先级仅次于1; - Handler的默认方法:
Handler.handleMessage(msg)
,优先级最低。
5. 其他方法
1.Looper.quit方法
java
public final class Looper
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
}
public final class MessageQueue {
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
// 队头还未到执行时间,那所有Message都未到,
// 队列里的Message都属于Future Message,
// 直接调用removeAllMessagesLocked即可。
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
// 到这里说明,队列中的Message都在投递状态,
// 都不属于Future Message,所以直接return。
if (n == null) {
return;
}
// 找到了还未到执行时间的msg,
// 那么它后面的肯定也都是Future msg
// 退出遍历循环即可。
if (n.when > now) {
break;
}
p = n;
}
// 此时的n属于找到的第一个Future Message
// 此时p 是 n的前一个Message
// 下面要把p后面,从n开始的msg全部回收
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
}
Loopr中quit 有两个方法,分别是 quit()
和 quitSafely()
,其最终会调用到 MessageQueue 的 quit 方法中,最终调用removeAllMessagesLocked或者removeAllFutureMessagesLocked。
removeAllMessagesLocked这个比较简单,遍历队列,对每个 Message 进行回收,最后将队头 mMessages 赋值为 null
removeAllFutureMessagesLocked移除的是还未到执行时间的 Message,如果 Message 已经到了执行时间,在投递阶段,还未派发完成,则需要执行完成。
三. 总结
1. 源码总结
再来总结下以上的所有内容
- 每个 Handler 都会和一个 Looper 实例关联在一起,可以在初始化 Handler 时通过构造函数主动传入实例,否则就会默认使用和当前线程关联的 Looper 对象。
- 每个 Looper 都会和一个 MessageQueue 实例关联在一起,每个线程都需要通过调用
Looper.prepare()
方法来初始化本线程独有的 Looper 实例,并通过调用Looper.loop()
方法来使得本线程循环向 MessageQueue 取出消息并执行。Android 系统默认会为每个应用初始化和主线程关联的 Looper 对象,并且默认就开启了 loop 循环来处理主线程消息。 - MessageQueue 按照链接结构来保存 Message,执行时间早(即时间戳小)的 Message 会排在链表的头部,Looper 会循环从链表中取出 Message 并回调给 Handler,取值的过程可能会包含阻塞操作。
- Message、Handler、Looper、MessageQueue 这四者就构成了一个生产者和消费者模式。Message 相当于产品,MessageQueue 相当于传输管道,Handler 相当于生产者,Looper 相当于消费者。
- Handler 对于 Looper、Handler 对于 MessageQueue、Looper 对于 MessageQueue、Looper 对于 Thread ,这几个之间都是一一对应的关系,在关联后无法更改,但 Looper 对于 Handler、MessageQueue 对于 Handler 可以是一对多的关系。
- Handler 能用于更新 UI 包含了一个隐性的前提条件:Handler 与主线程 Looper 关联在了一起。在主线程中初始化的 Handler 会默认与主线程 Looper 关联在一起,所以其
handleMessage(Message msg)
方法就会由主线程来调用。在子线程初始化的 Handler 如果也想执行 UI 更新操作的话,则需要主动获取 mainLooper 来初始化 Handler。 - 对于我们自己在子线程中创建的 Looper,当不再需要的时候我们应该主动退出循环,否则子线程将一直无法得到释放。对于主线程 Loop 我们则不应该去主动退出,否则将导致应用崩溃。
- 我们可以通过向 MessageQueue 添加 IdleHandler 的方式,来实现在 Loop 线程处于空闲状态的时候执行一些优先级不高的任务。例如,假设我们有个需求是希望当主线程完成界面绘制等事件后再执行一些 UI 操作,那么就可以通过 IdleHandler 来实现,这可以避免拖慢用户看到首屏页面的速度。
2. Handler 的同步机制
MessageQueue 在保存 Message 的时候,enqueueMessage
方法内部已经加上了同步锁,从而避免了多个线程同时发送消息导致竞态问题。此外,next()
方法内部也加上了同步锁,所以也保障了 Looper 分发 Message 的有序性。最重要的一点是,Looper 总是由一个特定的线程来执行遍历,所以在消费 Message 的时候也不存在竞态。
3. native方法
我们刚才遗留了三个native方法分别是:nativeInit
,nativeWake
,nativePollOnce
。
我们会在下一篇文章中继续介绍native
方法和epoll
机制。