汇总:Android小白成长之路_知识体系汇总【持续更新中…】
简述
Handler机制是Android中非常重要的一种消息处理机制,它的本质是消息的分发和处理。Android的同学们应该对其都不会陌生,但是有多少同学是知其然而不知其所以然的呢?包括以前的我,也只会背背八股文,但结果就是对Handler理解不够透彻。翻阅了不少网上的文章,都是大佬写的,所以有些细节直接跳过,大佬觉得不用说我们也懂,但我就是不懂。因此我决定对着源码开始一步步剖析,最终形成了这篇文章,有了这篇文章,要是面试被问到Handler相关的知识点,那还不是直接开始表演?话不多说,让我们开始深入了解一下Handler消息机制的整个流程吧!
先上个网上大佬总结的图(侵权请联系我删除):
Looper
我们创建Handler的时候,通常会写以下两行代码:
Looper.prepare();
Looper.loop();
prepare()
我们先看看Looper.prepare()
干了什么:
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));
}
这里的sThreadLocal
是什么?我们暂时只需要知道它是为了保证线程私有,也就是线程和Looper是一一对应的,这里先看new Looper(quitAllowed)
做了什么:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,这里先根据sThreadLocal
判断Looper
是否已经创建,如果没创建则创建了一个Looper
,然后把这个Looper
放在sThreadLocal
中,在Looper
的构造方法中创建MessageQueue
,然后获取了当前的线程mThread
,所以Looper.prepare()
的过程实际上就是创建Looper
和MessageQueue
还有使Looper
与当前线程进行关联
loop()
我们接着看Looper.loop()
干了什么:
public static void loop() {
//myLooper()方法其实就是返回sThreadLocal.get()的值,也就拿到前面创建好的Looper
final Looper me = myLooper();
//如果为空则抛出异常,这就是为啥要先调用Looper.prepare()的原因
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//从Looper中拿出创建好的MessageQueue
final MessageQueue queue = me.mQueue;
...
//开启死循环,这里抛出一个问题,既然是死循环,为啥不会ANR?
for (;;) {
//从MessageQueue中取出下一个Message,next()方法后续分析MessageQueue的时候再细说
Message msg = queue.next(); // might block
//如果设置了mLogging,会打印日志,多用于性能检测工具,比如blockcanary的原理就是使用了这个mLogging打印
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
try {
//调用handler的dispatchMessage进行消息处理,其中msg.target就是当前消息对应的handler
msg.target.dispatchMessage(msg);
} finally {
...
}
...
//消息分发处理完成,打印消息处理结束的日志
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
//消息处理完后回收到msg的池子里,等待后续Message.obtain()复用msg
msg.recycleUnchecked();
}
}
从上面代码我们可以得知,loop()
方法其实就是不断地从MessageQueue
里取出Message
并分发给Message
对应的Handler
的dispatchMessage()
方法去处理,消息使用完后回收到消息复用的池子里。
prepareMainLooper()
从上面我们已经分析了Looper.prepare()
和Looper.loop()
的源码,但我们发现,其实我们在主线程使用Handler
的时候并没有调用这两个方法,这是为什么呢?其实Android的启动流程中早早就帮我们做了这件事,我们打开ActivityThread
这个类,找到main()
方法:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
}
我们看看Looper.prepareMainLooper()
方法:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
上面的关键两个方法就是prepare()
和myLooper()
,这就和普通的创建Looper
的方式是一样的,这里把创建好的Looper
保存在了sMainLooper
成员变量中。我们知道ActivityThread
的main()
方法在app启动的时候就会去调用,所以是很早就已经创建好一个Looper
并且与主线程已经绑定好,而且开启了loop()
MessageQueue
前面写到在Looper
的构造函数中创建了MessageQueue
,我们看看创建MessageQueue
时做了什么:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
可以看到就对mQuitAllowed
进行了赋值和调用了nativeInit()
方法,nativeInit()
方法这里就不进行分析了,只需要知道它是为了能调用一些底层的处理机制就行了,例如后面的消息阻塞等待,感兴趣的可以去找相关代码查阅,而这里的mQuitAllowed
属性,其实只有一个地方用到:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
...
}
}
在调用MessageQueue
的quit()
方法时,会先判断这个属性,在主线程的时候这个属性是false的,也就是禁止我们调用主线程里消息队列的quit()
方法退出消息队列的消息处理流程
next()
在前面Looper的死循环中我们看到取消息的过程是调用了MessageQueue
的next()
方法,我们看看next()方法做了啥:
Message next() {
...
//IdleHandler的数量,IdleHandler是什么我们后面细说
int pendingIdleHandlerCount = -1;
//超时时间,用于判断是否需要阻塞
int nextPollTimeoutMillis = 0;
for (;;) {
...
//阻塞机制,nextPollTimeoutMillis值的意义
// 0 :立即返回,不进行阻塞
// -1 : 一直阻塞,直到有新的消息然后被唤醒
// 正数 : 表示最多阻塞等待多久时间
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;
//msg.target == null 即没有对应的handler,意味着插入了同步屏障,同步屏障后面细说
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 {
//如果没有消息,则把超时时间设置为-1
nextPollTimeoutMillis = -1;
}
//如果已经调用了quit()方法,则返回null
if (mQuitting) {
dispose();
return null;
}
//新一轮调用next()进来时pendingIdleHandlerCount是-1,因此需要重新查询IdleHandler的数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//如果没有需要执行的IdleHandler,则跳过
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//如果有需要执行的IdleHandler,则把这些IdleHandler转为数组,后续遍历数组执行
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 循环遍历IdleHandler数组
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//执行IdleHandler的queueIdle()方法,并返回keep值,用来判断是否执行后仍保留
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果不保留则移除这个IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置pendingIdleHandlerCount的值为0,那么下次循环的时候不会再获取IdleHandler的数量(pendingIdleHandlerCount<0才会获取,只有下次调用next()方法pendingIdleHandlerCount才会小于0),所以不会重复执行IdleHandler
pendingIdleHandlerCount = 0;
// 重置nextPollTimeoutMillis的值为0,也就是下次循环不会阻塞,主要是为了再次检测现在是否有可执行的消息
nextPollTimeoutMillis = 0;
}
}
这个方法里决定了当前需要阻塞还是执行某个消息还是执行IdleHandler
enqueueMessage()
当nativePollOnce
进行无限阻塞时,需要被唤醒才会继续循环,唤醒的方法在enqueueMessage()
方法中:
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;
//一直遍历到尾部或者找到一个时间比当前消息更小的消息,则插入在这个位置,因此msg的执行是有序的,是根据when来排序的
if (p == null || when < p.when) {
break;
}
//如果在之前已经存在异步消息,则不唤醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
// 唤醒阻塞的队列
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage()
方法负责插入消息,并根据情况决定是否需要唤醒next()
方法里的nativePollOnce()
阻塞
removeCallbacksAndMessages()
接下来我们看看消息的移除,移除消息有多个方法,我们这里选择分析以下移除所有消息的方法removeCallbacksAndMessages():
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// 先轮询队列,把target = handler并且object满足条件的msg节点移除(通常我们把object传入为null),并且找出mMessages对应的节点,也就是除去当前handler对应的msg后第一个别的msg的节点
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//继续轮询之后的节点,把满足条件msg节点移除
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
可以看到过程并不复杂,但是第一眼看可能有疑问,这两段移除节点的代码非常相似,为啥要写两遍呢?我们细心点可以发现,前面那段代码移除节点的同时把mMessages
赋值,后面那段并没有做这个操作。它们的区别就是前面的代码不仅移除节点,还要找到当前的头结点应该在的位置,后面的代码仅仅是进行删除节点而已
postSyncBarrier()
前面我们提到了同步屏障和异步消息的概念,那么它们到底是什么呢?又起到什么作用呢?我们知道,每个msg在队列中是按照when
排序执行的,那么如果我有某个消息需要尽快执行,可不可以有优先权?同步屏障和异步消息就给予了我们这个权利。例如在屏幕刷新流程中,假设60Hz的屏幕刷新率,那么就是16ms发送一次Vsync
信号,在这个时间内我们的绘制工作要执行完毕,否则可能会导致丢帧,而我们的绘制工作也是通过发送特定Message
给Handler
处理的,所以这些Message
应该得到优先执行,因此同步屏障和异步消息在屏幕刷新机制中尤为重要。
插入同步屏障需要调用postSyncBarrier()
方法,接下来我们看看postSyncBarrier()
的源码:
public int postSyncBarrier() {
//这里把when设为SystemClock.uptimeMillis(),这个是开机到现在这一刻的时间,这样可以确保这条消息看可以插入消息队列中较前的位置,没有任何延迟
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
//生成一个token用来唯一表示当前这个同步屏障,后续同步屏障移除的时候需要
final int token = mNextBarrierToken++;
//注意,下面的msg属性赋值中并没有设置msg.target的值,也就是当前消息的msg.target = null
//而这个msg.target = null的消息,我们就称之为同步屏障,细心的同学可能会想起之前分析next()方法 //取消息的时候,就有一段是处理同步屏障情况的:
// if (msg != null && msg.target == null) {
// //轮询查找异步消息
// do {
// prevMsg = msg;
// msg = msg.next;
// } while (msg != null && !msg.isAsynchronous());
// }
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
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;
}
//返回唯一的token
return token;
}
}
从上面的代码我们知道,其实同步屏障和普通消息的差异仅仅是同步屏障的target
为null
,仅此而已,当target
为null
时,next()
方法取消息的时候就会去轮询查找msg.isAsynchronous()
为true
的msg,而这个msg.isAsynchronous()
为true
的消息,就是所谓的异步消息。这样就做到了通过同步屏障阻断同步消息,直接执行异步消息,但最后一定要移除同步屏障,不然同步消息就永远得不到执行了,至于如何把消息转设置为异步消息,后续细说
removeSyncBarrier()
我们上面说了插入同步屏障执行异步消息之后要记得移除同步屏障,移除同步屏障的方法为removeSyncBarrier()
,我们看看其做了什么:
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
//轮询找出target=null并且token等于传进来的token的消息,这个消息就是我们需要移除的同步屏障
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
//prev != null,说明同步屏障之前也有别的消息存在,尚未堵塞,无需唤醒
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
//如果没有消息了,需要唤醒一下阻塞,为了检测执行IdleHandler
//或者如果同步屏障之后还有消息,并且不是另一个同步屏障,也需要唤醒队列去执行
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// 如果需要唤醒阻塞队列,则进行唤醒
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
IdleHandler
前面多次提到IdleHandler
,那么IdleHandler
到底是什么呢?我们看看IdleHandler
的源码:
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
可以看到,它其实就是一个接口,并且只有一个方法,这个方法其实我们前面的next()方法中已经见到过了,我们再回顾一下代码:
Message next() {
...
for (;;) {
...
synchronized (this) {
if (msg != null) {
...
return msg;
}
} else {
//如果没有消息,则把超时时间设置为-1
nextPollTimeoutMillis = -1;
}
...
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 循环遍历IdleHandler数组
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//执行IdleHandler的queueIdle()方法,并返回keep值,用来判断是否执行后仍保留
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果不保留则移除这个IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
...
}
}
在消息队列中没有消息的时候,就会去查找mPendingIdleHandlers
,这是一个IdleHandler
类型的数组,它的数据从mIdleHandlers
中转换得来,mIdleHandlers
是一个存放IdleHandler
的list集合。我们从数组中遍历取出IdleHandler
,然后执行它的queueIdle()
方法。因此如果我们想在App空闲时候再去做某些任务,可以把这些任务放在queueIdle()
方法中。例如gc回收,其实就利用了这个机制
添加一个IdleHandler
和移除一个IdleHandler
都很简单,其实就是往mIdleHandlers
里add
和remove
就行了,这里贴一下相关代码:
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
Message
前面已经很多次提到Message
,其实Message
就是我们要发送给Handler处理的数据对象,我们看看它有哪些关键的属性:
public final class Message implements Parcelable {
//标识要处理的message类型,可以根据这属性个处理不同的场景需求
public int what;
//快捷存储一个int类型数据
public int arg1;
//另一个快捷存储的int类型数据
public int arg2;
//传递的对象数据
public Object obj;
//正在被使用的标记
/*package*/ static final int FLAG_IN_USE = 1 << 0;
//标记是否是异步消息
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
//标记是否正在被使用或者是同步或异步消息
/*package*/ int flags;
//执行时间
/*package*/ long when;
//传递bundle类型的数据
/*package*/ Bundle data;
//指向对应的handler或者同步屏障null
/*package*/ Handler target;
//准备执行的Runnable
/*package*/ Runnable callback;
//指向下一条消息
/*package*/ Message next;
//可复用的消息池节点
private static Message sPool;
//当前可复用的消息池节点数
private static int sPoolSize = 0;
//消息池最大可复用消息数
private static final int MAX_POOL_SIZE = 50;
}
obtain()
我们知道,Message.obtain()
方法可以返回一个消息,官方也推荐我们使用这种方式来获取新的消息,给出的原因是这样可以复用之前的消息,我们看看是怎么回事,这里我们选择一个无参obtain()
方法,看看它的实现:
public static Message obtain() {
synchronized (sPoolSync) {
//消息复用池的头节点不为null,说明存在可复用的消息
if (sPool != null) {
Message m = sPool;
//把头节点指向它的下一个节点
sPool = m.next;
//断开头节点
m.next = null;
m.flags = 0; // clear in-use flag
//消息复用池大小减一
sPoolSize--;
//返回这个取出的节点
return m;
}
}
//如果消息复用池为空,则new一个Message
return new Message();
}
可以看到,其实就是取消息复用池的头节点,如果这个节点为null,说明没有可复用的消息,那么就新建一个消息并返回。那么消息复用池的节点哪来的呢?我们接着往下分析
recycleUnchecked()
我们在前面分析消息队列移除消息的时候,有看到移除节点那里使用了recycleUnchecked()
这个方法,我们看看它做了什么:
void recycleUnchecked() {
//重置各种属性到原始状态
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//如果当前消息复用池的大小小于复用池的最大值
//则把当前消息插入到消息复用池的头节点之前,作为新的头节点,复用池大小加一
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
这个消息的回收代码也很简单,只是把消息重置成初始状态,然后链接到复用池的链表中作为头节点而已
setAsynchronous()、isAsynchronous()
我们再看看和异步消息相关的方法,首先看看setAsynchronous()
方法:
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
其实就是改变一下flags的值,根据async把flags标记为异步或者同步
然后isAsynchronous()
主要就是为了判断当前消息是异步还是同步:
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
Handler
前面分析了这么多源码,终于要到Handler了,天天喊Handler,那么Handler到底干了啥呢?其实Handler机制中复杂的部分前面已经说完了,Handler这里反而是较为简单的部分,我们先看看Handler的几个构造方法:
//无参构造方法,其实就是调用了Handler(Callback callback, boolean async),默认不带Callback,并且是同步消息
public Handler() {
this(null, false);
}
//一样调用了Handler(Callback callback, boolean async),带Callback,同步消息
public Handler(Callback callback) {
this(callback, false);
}
//调用了Handler(Looper looper, Callback callback, boolean async),带looper,不带callback,是同步消息
public Handler(Looper looper) {
this(looper, null, false);
}
//调用了Handler(Looper looper, Callback callback, boolean async),带looper,带callback,是同步消息
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
//调用了Handler(Callback callback, boolean async),带Callback,参数控制是同步还是异步
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
...
//没传looper,所以直接获取当前所在线程的looper
mLooper = Looper.myLooper();
//如果获取不到looper,报异常,需要先调用Looper.prepare()创建looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//拿到消息队列,拿到callback和是否异步的标记
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到,并不复杂,就是赋值一下mLooper
、mQueue
、mCallback
和mAsynchronous
sendMessage()
我们看看发送消息的流程,首先是最基本的sendMessage()
:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
其实就是调用了sendMessageDelayed()
,并把延迟设为0
sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
简直就是套娃啊,简单处理了一下delayMillis
后,调用sendMessageAtTime()
,这里的时间需要注意,它是SystemClock.uptimeMillis() + delayMillis
,也就是当前开机时间加上延迟时间,我们前面分析消息执行流程的时候知道使用的时间都是SystemClock.uptimeMillis()
,所以这里对应上了
sendMessageAtTime()
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);
}
这里稍作处理后继续套娃,简单来说就是调用enqueueMessage()
方法
enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里首先把当前Handler
的实例赋值给msg.target
,然后判断了一下是否是异步消息,设置相应的标记,最后就调用了MessageQueue
的enqueueMessage()
方法插入消息到消息队列,这一步前面分析MessageQueue
的时候说过了不再复述
至于sendEmptyMessage()的流程,和上面基本一直,只是在封装好的代码里自己写了Message的创建,不需要我们来创建而已,这里不展开说了。
接下来我们探讨一个问题,我们知道sendMessage()
的时候把delayMillis
默认设为了0,那么这个消息会马上执行吗?回顾之前插入消息队列的知识,我们知道插入消息的位置是和when
属性相关的,这里我们把delayMillis
设置为0,但是在sendMessageAtTime
的时候又修改为SystemClock.uptimeMillis() + delayMillis
,那么这里的时间肯定比上一次调用sendMessage()
的那个消息的时间更晚,所以它并不会先于之前的消息执行。那我就是想要它比之前的更快执行怎么办?
我们看这样一个方法:
public final boolean sendMessageAtFrontOfQueue(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;
}
return enqueueMessage(queue, msg, 0);
}
从名字上我们就能知道这个消息是要插入到消息队列最前端的,说明是可以提高这个消息优先级的,结果一看。其实就是enqueueMessage()
的时候把延迟真正设为0了,所以这个方法能实现提高消息执行的优先级。看到这里,我们可以想到前面的sendMessageAtTime()
最终也是调用enqueueMessage()
,并传入uptimeMillis
,那么我们只需要把uptimeMillis
传入0就行了
post()
我们知道,Handler
除了可以处理Message,还能处理Runnable
,我们看看post
的实现:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
又开始套娃了,调用了sendMessageDelayed()
,这个方法的后续前面已经分析过了。我们看看getPostMessage()
方法做了什么:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
原来是偷偷地创建了一个Message
,并且设置它的callback
为传入的Runnable
,从这里我们可以得知,无论哪种方式发送,最终都是以发送Message
的形式发送的
dispatchMessage()
在前面分析Looper.loop()
的时候,我们通过MessageQueue.next()
方法获取Massage
后,就会调用它的dispatchMessage()
方法去进行消息的分发,我们看看dispatchMessage()
做了什么:
public void dispatchMessage(Message msg) {
//如果msg的callback不为null,则调用handleCallback,里面其实就是调用Runnable的run方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//这里的mCallback是使用Handler构造函数传进来的那个
if (mCallback != null) {
//执行构造函数传进来的Callback回调
if (mCallback.handleMessage(msg)) {
return;
}
}
//否则调用handleMessage
handleMessage(msg);
}
}
可以看到,优先执行msg
里的callback
,没有的话再执行构造方法传进来的mCallback
,都没有的话才执行handleMessage()
方法
总结
到这里,我们就把Handler相关的源码基本上过了一遍,建议对照着源码自己也梳理一遍,弄清楚每一步的意图,不要一知半解就跳过了,当真正读懂每一步源码,才能在脑海中形成最佳的知识体系。有一位大佬曾对我说过,网络上的文章可能会骗人,但源码肯定不会骗你。
所以我总结出一句话:遇事不决看源码!