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。
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来取消息,处理消息。
- 当遇到同步屏障消息的时候,就开始从消息队列里面去找异步消息,找到了再根据时间决定阻塞还是返回消息。
也就是说同步屏障消息不会被返回,它只是一个标志,一个工具,遇到它就代表要去先行处理异步消息了。所以同步屏障和异步消息的存在的意义就在于有些消息需要“加急处理”
应用场景:
UI绘制中:scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
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方法停止消息循环。
- 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) {
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();
}
所以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) {
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
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 (;;) {
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();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
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);
}
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) {
.................................
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue;
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) {
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(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到sendMessageAtTime() 方法的作用很简单,就是调用MessageQueue 的 enqueueMessage() 方法,往消息队列中添加一个消息。 下面来看enqueueMessage() 方法的具体执行逻辑。 enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
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.recycle();
return false;
}
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;
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;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
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) {
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;
msg.markInUse();
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
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) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
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() ,一般地往往通过覆写该方法从而实现自己的业务逻辑
|