1.Handler消息模型图
主要包含的关键类:
MessageQueue,Handler 和 Looper 这三大部分,以及 Message
- Message:需要传递的消息,可以传递数据;
- MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
- Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
- Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
三者之间的主要关系:
每个线程中只能存在一个Looper,Looper是保存在 ThreadLocal 中的。主线程(UI线程)在main 方法中已经自动创建了一个Looper,在其他线程中需要手动创建 Looper。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。
2.必知题目
Handler 设计理念
1.Handler 存在的意义,为什么被这样设计,有什么用?
Handler主要作用就是用来切换线程,它管理着所有与界面有关的消息事件。
总结一句话:
Hanlder
的存在就是为了解决在子线程中无法访问 UI 的问题。
2.为什么不能在子线程中更新 UI,真的不能更新么?
Android中的UI控件访问是非线程安全的。
如果加锁呢?
- 会降低UI访问的效率:本身UI控件就是离用户比较近的一个组件,加锁之后自然会发生阻塞,那么UI访问的效率会降低,最终反应到用户端就是这个手机有点卡
Android设计出了
单线程模型
来处理UI操作,再搭配上Handler,是一个比较合适的解决方案。
3.子线程更新 UI 出现崩溃的原因,怎么解决?
崩溃发生在 ViewRootImpl 类的
checkThread
方法中:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
其实就是判断了当前线程 是否是
ViewRootImpl
创建时候的线程,如果不是,就会崩溃。而 ViewRootImpl 创建的时机就是界面被绘制的时候,也就是onResume之后,所以如果在子线程进行UI更新,就会发现当前线程(子线程)和 View 创建的线程(主线程)不是同一个线程,发生崩溃。
解决办法:
- 在新建视图的线程进行这个视图的UI更新,主线程创建View,主线程更新View。
- 在
ViewRootImpl
创建之前进行子线程的UI更新,比如onCreate方法中进行子线程更新UI。 - 子线程切换到主线程进行UI更新,比如
Handler、view.post
方法(推荐)。
MessageQueue 相关:
1.MessageQueue 的作用,及数据结构?
Android
中采用了链表的形式来实现这个队列,也方便了数据的插入和删除。无论是哪种方法发送消息,都会走到sendMessageDelayed
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
消息插入的过程:
首先设置了Message的when字段,也就是代表了这个消息的处理时间,然后判断当前队列是不是为空,是不是即时消息,是不是执行时间when大于表头的消息时间,满足任意一个,就把当前消息msg插入到表头。
否则,就需要遍历这个队列,也就是链表,找出when小于某个节点的when,找到后插入。插入消息就是通过消息的执行时间,也就是when字段,来找到合适的位置插入链表。具体方法就是通过死循环,使用快慢指针 p 和 prev,每次向后移动一格,直到找到某个节点p的when大于我们要插入消息的when字段,则插入到p和prev之间。或者遍历到链表结束,插入到链表结尾。所以,MessageQueue就是一个用于存储消息、用链表实现的特殊队列结构。
2.延迟消息是怎么实现的?
无论是即时消息还是延迟消息,都是计算出具体的时间,然后作为消息的when字段进程赋值。然后在MessageQueue中找到合适的位置(安排when小到大排列),并将消息插入到
MessageQueue
中。这样,MessageQueue
就是一个按照消息时间排列的一个链表结构。
3.MessageQueue的消息是如何取出来的?
内部通过 next() 死循环来获取消息,死循环就是保证一定要返回一条消息。如果没有可用消息,那么就阻塞在这里,一直到有新消息的到来。
其中,
nativePollOnce
方法就是阻塞方法,nextPollTimeoutMillis
就是阻塞的时间
那什么时候会阻塞呢?两种情况:
1、有消息,但是当前时间小于消息执行时间
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}
//这时候阻塞时间就是消息时间减去当前时间,然后进入下一次循环,阻塞。
2、没有消息的时候
nextPollTimeoutMillis = -1;//表示一直阻塞
4.MessageQueue没有消息的时候会怎样,阻塞之后如何唤醒?及 pipe/epoll 机制?
当消息不可用或者没有消息的时候就会阻塞在next方法,而阻塞的办法是通过 pipe/epoll 机制
是一种IO多路复用的机制,具体逻辑就是一个进程可以监视多个描述符,当某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作,这个读写操作是阻塞的。在Android中,会创建一个
Linux管道(Pipe)
来处理阻塞和唤醒。
- 当消息队列为空,管道的读端等待管道中有新内容可读,就会通过
epoll
机制进入阻塞状态。- 当有消息要处理,就会通过管道的写端写入内容,唤醒主线程。
5.同步屏障和异步消息是怎么实现的,已经它们的应用场景是怎样的?
Handler中有三种消息类型:
- 同步消息 :即普通消息
- 异步消息:通过 setAsynchronous(true)设置的消息
- 同步屏障消息:通过postSyncBarrier方法添加的消息,特点是targe t为空,也就是没有对应的handler。
这三者之间的关系如何呢?
- 正常情况下,同步消息和异步消息都是正常被处理,也就是根据时间when来取消息,处理消息。
- 当遇到同步屏障消息的时候,就开始从消息队列里面去找异步消息,找到了再根据时间决定阻塞还是返回消息。
也就是说同步屏障消息不会被返回,它只是一个标志,一个工具,遇到它就代表要去先行处理异步消息了。所以同步屏障和异步消息的存在的意义就在于有些消息需要
“加急处理”
应用场景:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 同步屏障,阻塞所有的同步消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 通过 Choreographer 发送绘制任务
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
6.Message消息被分发之后怎么处理,消息是如何复用的?
在执行完 dispatchMessage 后会有: msg.recycleUnchecked(),将 msg 的所有参数制空,释放了所有资源,将当前的空消息插入到sPool表头。
sPool
就是一个消息对象池,它也是一个链表结构的消息,最大长度为50。
**消息复用过程:**直接从消息池中获取,如果获取不到才会重新创建。
Looper相关:
1.Looper的作用?怎么获取当前线程的 Looper?为什么不直接用 Map 存储线程和对象呢?
Looper
就是一个管理消息队列的角色。会从 MessageQueue 中不断的查找消息,也就是 loop 方法,并将消息交回给 Handler 进行处理,而 Looper就是从 ThreadLocal 中获取。
2.ThreadLocal的运行机制?这种设计有什么好处?
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在每个线程中都有一个threadLocals变量,这个变量存储着 ThreadLocal 和对应的需要保存的对象。这样带来的好处就是,在不同的线程,访问同一个ThreadLocal对象,但是能获取到的值却不一样。其内部获取到的Map不同,Map和Thread绑定,所以虽然访问的是同一个
ThreadLocal
对象,但是访问的Map却不是同一个,所以取得值也不一样。
为什么不直接用Map存储线程和对象呢?
打个比方:
ThreadLocal
就是老师。Thread
就是同学。Looper
(需要的值)就是铅笔。现在老师买了一批铅笔,然后想把这些铅笔发给同学们,怎么发呢?两种办法:
- 1、老师把每个铅笔上写好每个同学的名字,放到一个大盒子里面去(map),用的时候就让同学们自己来找。
这种做法就是Map里面存储的是
同学和铅笔
,然后用的时候通过同学来从这个Map里找铅笔。这种做法就有点像使用一个Map,存储所有的线程和对象,不好的地方就在于会很混乱,每个线程之间有了联系,也容易造成内存泄漏。
- 2、老师把每个铅笔直接发给每个同学,放到同学的口袋里(map),用的时候每个同学从口袋里面拿出铅笔就可以了。
这种做法就是Map里面存储的是
老师和铅笔
,然后用的时候老师说一声,同学只需要从口袋里拿出来就行了。很明显这种做法更科学,这也就是ThreadLocal
的做法,因为铅笔本身就是同学自己在用,所以一开始就把铅笔交给同学自己保管是最好的,每个同学之间进行隔离。
3.还有哪些场景会使用到 ThreadLocal?
Choreographer
主要是主线程用的,用于配合VSYNC
中断信号。所以这里使用ThreadLocal
更多的意义在于完成线程单例的功能。
4.Looper的创建方式,quitAllow字段的作用?
同一个线程,只能创建一个
Looper
,多次创建会报错。quitAllow 是是否允许退出。
quit
方法一般是什么时候使用呢?
- 主线程中,一般情况下肯定不能退出,因为退出后主线程就停止了。所以是当APP需要退出的时候,就会调用quit方法,涉及到的消息是EXIT_APPLICATION,大家可以搜索下。
- 子线程中,如果消息都处理完了,就需要调用quit方法停止消息循环。
5.Looper内部是死循环,为什么不会卡死?
- 1、主线程本身就是需要一只运行的,因为要处理各个View,界面变化。所以需要这个死循环来保证主线程一直执行下去,不会被退出。
- 2、真正会卡死的操作是在某个消息处理的时候操作时间过长,导致掉帧、ANR,而不是loop方法本身。
- 3、在主线程以外,会有其他的线程来处理接受其他进程的事件,比如
Binder线程(ApplicationThread)
,会接受AMS发送来的事件- 4、在收到跨进程消息后,会交给主线程的
Hanlder
再进行消息分发。所以Activity的生命周期都是依靠主线程的Looper.loop
,当收到不同Message时则采用相应措施,比如收到msg=H.LAUNCH_ACTIVITY
,则调用ActivityThread.handleLaunchActivity()
方法,最终执行到onCreate方法。- 5、当没有消息的时候,会阻塞在loop的
queue.next()
中的nativePollOnce()
方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生。所以死循环也不会特别消耗CPU资源。
Handler相关:
1.Message是如何进行消息分发的,怎么与 Handler进行绑定?
通过 msg.target.dispatchMessage(msg) 来分发消息。
在使用Hanlder发送消息的时候,会设置
msg.target = this
,所以target就是当初把消息加到消息队列的那个Handler。
2.Handler 中 post 和 sendMessage 区别?
Hanlder中主要的发送消息可以分为两种:
- post(Runnable)
- sendMessage
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
post和sendMessage
的区别就在于:post方法给Message设置了一个callback
。
息处理的方法dispatchMessage
:
public void dispatchMessage(@NonNull Message msg) {
//1.如果 msg.callback 不为 null,通过post方法发送消息的时候,会把消息交给这个msg.callback进行处理,然后就没有后续了。
if (msg.callback != null) {
handleCallback(msg);
} else {
//2.如果msg.callback为空,也就是通过sendMessage发送消息的时候,会判断Handler当前的mCallback是否为空,如果不为空就交给Handler.Callback.handleMessage处理。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//3.如果mCallback.handleMessage返回false,则调用handler类重写的handleMessage方法。
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
所以post(Runnable) 与 sendMessage的区别就在于后续消息的处理方式,是交给
msg.callback
还是Handler.Callback
或者Handler.handleMessage
。
3.Handler.Callback.handleMessage 和 Handler.handleMessage 的区别?
区别在于
Handler.Callback.handleMessage
方法是否返回true:
- 如果为
true
,则不再执行Handler.handleMessage- 如果为
false
,则两个方法都要执行。
拓展:
1.IdleHandler 作用及使用场景?
当
MessageQueue
没有消息的时候,就会阻塞在next方法中,其实在阻塞之前,MessageQueue
还会做一件事,就是检查是否存在IdleHandler
,如果有,就会去执行它的queueIdle
方法。
private IdleHandler[] mPendingIdleHandlers;
Message next() {
int pendingIdleHandlerCount = -1;
for (;;) {
synchronized (this) {
//1. 当消息执行完毕,即当前线程处于空闲状态,就设置pendingIdleHandlerCount
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//2.初始化mPendingIdleHandlers
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//mIdleHandlers转为数组
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 {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果queueIdle方法返回false,则处理完就删除这个IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
}
}
当没有消息处理的时候,就会去处理这个
mIdleHandlers
集合里面的每个IdleHandler
对象,并调用其queueIdle
方法。最后根据queueIdle
返回值判断是否用完删除当前的IdleHandler
。
IdleHandler
就是当消息队列里面没有当前要处理的消息了,需要阻塞之前,可以做一些空闲任务的处理。
常见应用场景:
启动优化
。
- 我们一般会把一些事件(比如界面view的绘制、赋值)放到
onCreate
方法或者onResume
方法中。但是这两个方法其实都是在界面绘制之前调用的,也就是说一定程度上这两个方法的耗时会影响到启动时间。所以我们可以把一些操作放到IdleHandler
中,也就是界面绘制完成之后才去调用,这样就能减少启动时间了
2.HandlerThread原理及使用场景?
public class HandlerThread extends Thread {
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
}
}
HandlerThread
就是一个封装了Looper的Thread类。就是为了让我们在子线程里面更方便的使用Handler。
这里的加锁就是为了保证线程安全,获取当前线程的Looper对象,获取成功之后再通过
notifyAll
方法唤醒其他线程,
3.IntentService 原理及使用场景?
- 首先,这是一个
Service
- 并且内部维护了一个
HandlerThread
,也就是有完整的Looper在运行。 - 还维护了一个子线程的
ServiceHandler
。 - 启动Service后,会通过Handler执行
onHandleIntent
方法。 - 完成任务后,会自动执行
stopSelf
停止当前Service。
所以,这就是一个可以在子线程进行耗时任务,并且在任务执行后自动停止的Service
4.BlockCanary的原理?
BlockCanary
是一个用来检测应用卡顿耗时的三方库。
public static void loop() {
for (;;) {
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
}
}
loop方法内有一个
Printer
类,在dispatchMessage
处理消息的前后分别打印了两次日志。把这个日志类Printer
替换成我们自己的Printer
,
5.Handler的内存泄露问题?
6.如何利用Handler设计一个不崩溃的 App?
主线程崩溃,其实都是发生在消息的处理内,包括生命周期、界面绘制。所以如果我们能控制这个过程,并且在发生崩溃后重新开启消息循环,那么主线程就能继续运行
Handler(Looper.getMainLooper()).post {
while (true) {
//主线程异常拦截
try {
Looper.loop()
} catch (e: Throwable) {
}
}
}
3.源码解析
1.Looper
要想使用消息机制,首先要创建一个Looper。 初始化Looper 无参情况下,默认调用
prepare(true);
表示的是这个Looper可以退出,而对于false的情况则表示当前Looper不可以退出。
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,只能创建一个。创建Looper,并保存在ThreadLocal。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。
开启Looper
public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { //进入loop的主循环方法
Message msg = queue.next(); //可能会阻塞,因为next()方法可能会无限循环
if (msg == null) { //消息为空,则退出循环
return;
}
//`BlockCanary`是一个用来检测应用卡顿耗时的三方库。就是设置了自定义的 Printer
Printer logging = me.mLogging; //默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg); //获取msg的目标Handler,然后用于分发Message
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
}
msg.recycleUnchecked();
}
}
loop()进入循环模式,不断重复下面的操作,直到消息为空时退出循环: 读取MessageQueue的下一条Message(关于next(),后面详细介绍); 把Message分发给相应的target。
当next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞。等待MessageQueue中加入消息,然后重新唤醒。
主线程中不需要自己创建Looper,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()
方法。查看ActivityThread中的main()
方法,代码如下所示:
public static void main(String[] args) {
..........................
Looper.prepareMainLooper();
..........................
Looper.loop();
..........................
}
其中prepareMainLooper()
方法会调用prepare(false)
方法。
2.Handler
创建Handler
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
.................................
//必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
mLooper = Looper.myLooper(); //从当前线程的TLS中获取Looper对象
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue; //消息队列,来自Looper对象
mCallback = callback; //回调方法
mAsynchronous = async; //设置消息是否为异步处理方式
}
对于Handler的无参构造方法,默认采用当前线程TLS中的Looper对象,并且callback回调方法为null,且消息为同步处理方式。只要执行的Looper.prepare()
方法,那么便可以获取有效的Looper对象。
3.发送消息
发送消息有几种方式,但是归根结底都是调用了
sendMessageAtTime()
方法。在子线程中通过Handler的post()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()
方法。
post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
send方法
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
就连子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用
sendMessageAtTime()
方法。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,最终会调用
sendMessageAtTime()
方法。否则就直接调用Runnable对象的run()方法。下面我们就来一探究竟,到底sendMessageAtTime()
方法有什么作用?
sendMessageAtTime()
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//其中mQueue是消息队列,从Looper中获取的
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//调用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到sendMessageAtTime()
方法的作用很简单,就是调用MessageQueue 的 enqueueMessage()
方法,往消息队列中添加一个消息。
下面来看enqueueMessage()
方法的具体执行逻辑。
enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
// 每一个Message必须有一个target
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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 {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
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是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
4.获取消息
当发送了消息后,在MessageQueue维护了消息队列,然后在Looper中通过loop()
方法,不断地获取消息。上面对loop()
方法进行了介绍,其中最重要的是调用了queue.next()
方法,通过该方法来提取下一条信息。下面我们来看一下next()
方法的具体流程。
next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) { //当消息循环已经退出,则直接返回
return null;
}
int pendingIdleHandlerCount = -1; // 初始值为 -1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//当消息Handler为空时,查询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;
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//没有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
...............................
}
}
nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。 可以看出
next()
方法根据消息的触发时间,获取下一条需要执行的消息,队列中消息为空时,则会进行阻塞操作。
5.分发消息
在loop()方法中,获取到下一条消息后,执行
msg.target.dispatchMessage(msg)
,来分发消息到目标Handler对象。
下面就来具体看下dispatchMessage(msg)
方法的执行流程。
dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//当Message存在回调方法,回调msg.callback.run()方法;
handleCallback(msg);
} else {
if (mCallback != null) {
//当Handler存在Callback成员变量时,回调方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回调方法handleMessage()
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
分发消息流程:
当Message的
msg.callback
不为空时,则回调方法msg.callback.run()
;
当Handler的mCallback
不为空时,则回调方法mCallback.handleMessage(msg)
;
最后调用Handler自身的回调方法handleMessage()
,该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。
消息分发的优先级:
Message的回调方法:
message.callback.run()
,优先级最高;
Handler中Callback的回调方法:Handler.mCallback.handleMessage(msg)
,优先级仅次于1;
Handler的默认方法:Handler.handleMessage(msg)
,优先级最低。对于很多情况下,消息分发后的处理方法是第3种情况,即
Handler.handleMessage()
,一般地往往通过覆写该方法从而实现自己的业务逻辑