1、Handler机制简介
handler/Looper/Message/MessageQueue/thread
首先熟悉下怎么使用Handler?
java
//主线程
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case UPDATETEXT:
Log.e("handler","做ui线程中的事");
break;
default:
break;
}
}
};
//子线程:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "";
message.what = UPDATETEXT;
mHandler.sendMessage(message);
}
}).start();
}
什么是Handler
Handler通常的来说,就是我们在线程之间处理消息通知及任务调度的工具。
2、Handler被设计出来的原因?有什么用?
Handler的意义就是切换线程。作为Android消息机制的主要成员,它管理着所有与界面有关的消息事件。
常见的使用场景有:
1、跨进程之后的界面消息处理
比如Activity的启动,就是AMS在进行进程间通信的时候,通过Binder线程将消息发送给ApplicationThread的消息处理者Handler,然后再将消息分发给主线程中去执行。
2、网络交互后切换到主线程进行UI更新
当子线程网络操作之后,需要切换到主线程进行UI更新。
总之一句话,Hanlder的存在就是为了解决在子线程中无法访问UI的问题。
3、为什么建议子线程不访问(更新)UI?
因为Android中的UI控件不是线程安全的,如果多线程访问UI控件那还不乱套了。
那为什么不加锁呢?
- 会降低UI访问的效率。本身UI控件就是离用户比较近的一个组件,加锁之后自然会发生阻塞,那么UI访问的效率会降低,最终反应到用户端就是这个手机有点卡。
- 太复杂了。本身UI访问时一个比较简单的操作逻辑,直接创建UI,修改UI即可。如果加锁之后就让这个UI访问的逻辑变得很复杂,没必要。
所以,Android设计出了单线程模型来处理UI操作,再搭配上Handler,是一个比较合适的解决方案。
4、子线程访问UI的 崩溃原因 和 解决办法?
- 崩溃发生在ViewRootImpl类的checkThread方法中:其实就是判断了当前线程是否是ViewRootImpl创建时候的线程,如果不是,就会崩溃。
- 而ViewRootImpl创建的时机就是界面被绘制的时候,也就是onResume之后,所以如果在子线程进行UI更新,就会发现当前线程(子线程)和View创建的线程(主线程)不是同一个线程,发生崩溃。
java
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
解决办法有三种:
- 在新建视图的线程进行这个视图的UI更新,主线程创建View,主线程更新View。
- 在ViewRootImpl创建之前进行子线程的UI更新,比如onCreate方法中进行子线程更新UI。
- 子线程切换到主线程进行UI更新,比如Handler、view.post方法。
5、handler的工作机制?
-
相关类
Handler机制的实现离不开与之相关的其他三个类,Message是Handler发送的消息实体,大部分的消息都是通过Message来封装传递的;MessageQueue是用来将消息按顺序排队的队列;Looper本质就是一个循环,不停的从MessageQueue中取出消息然后处理。
-
执行过程
- 首先,如上图所示任务的开始是由创建一个Message开始的,Message创建完毕后交给Handler对象发送,sendMessage和sendMessageDelay最终都是在底层调用了sendMessageAtTime()方法,将Message对象放入MessageQueue中的。
- 之后,由Looper的loop()方法循环从MessageQueue中取出Message对象,调用message.getTarget()获取到发送消息的Handler对象,然后再调用handler.dispatchMessage()方法将信息分发给对应handler执行。
- 最后,Handler在dispatchMessage()方法中判断是否有callback 存在,存在则执行callback的onMessageHandler(),最终交由Message.callback执行;否则则执行handler的onMessageHandler()方法。
6、消息是怎么发送到MessageQueue中的?
Message创建完毕后交给Handler对象发送,sendMessage和sendMessageDelay最终都是在底层调用了sendMessageAtTime()方法,将Message对象调用到MessageQueue的queueMessage()放入MessageQueue中的。
java
Handler类
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
final MessageQueue mQueue;
final Looper mLooper;
public Handler(@Nullable Callback callback, boolean async) {
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = callback;
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);
}
Looper类
final MessageQueue mQueue;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
MessageQueue
Handler target;
7、消息队列中的消息是如何存储的?为什么没有用"常规容器类"。
链表特点
- 利用单链表进行存储,链表是一种非线性,非顺序的物理结构,由n个节点组成。
- 链表采用的"见缝插针"的存储方式,不需要内存连续,靠next指针关联;
- 存储的时候是随机的,访问方式是顺序访问;
7.1 为什么采用链表结构存储消息,而不是用数组结构?
1、因为我们执行过程中,堆里面可能已经建了很多个对象,如果我们初始化一个数组时候,可能空间不够,也没办法很好的利用碎片空间。
2、Handler消息存储与读取应该遵循先进先出,一般在队尾增加数据,在队首进行取数据或者删除数据。先发的消息肯定就会先被处理。
但是,Handler中还有比较特殊的情况,比如延时消息。延时消息的存在就让这个队列有些特殊性了,并不能完全保证先进先出,而是需要根据时间来判断,所以Android中采用了链表的形式来实现这个队列,也方便了数据的插入。
7.2 具体的消息存储过程?
- 消息的发送过程,无论是哪种方法发送消息,都会走到sendMessageDelayed方法,最后调用sendMessageAtTime方法。sendMessageDelayed方法主要计算了消息需要被处理的时间,如果delayMillis为0,那么消息的处理时间就是当前时间。
- 然后就是关键方法MessageQueue的enqueueMessage方法。
- 首先设置了Message的when字段,也就是代表了这个消息的处理时间
- 然后判断当前队列是不是为空,是不是即时消息,是不是执行时间when小于表头的消息时间,满足任意一个,就把当前消息msg插入到表头。
- 否则,就需要遍历这个队列,也就是链表,找出when小于某个节点的when,找到后插入。
- 总之,插入消息就是通过消息的执行时间,也就是when字段,来找到合适的位置插入链表。
- 具体方法就是通过for死循环,使用快慢指针p和prev,每次向后移动一格,直到找到某个节点p的when大于我们要插入消息的when字段,则插入到p和prev之间。或者遍历到链表结束,插入到链表结尾。
- 所以,MessageQueue就是一个用于存储消息、用链表实现的特殊队列结构。
8、延迟消息是怎么实现的?
- 延迟消息的实现主要跟消息的统一存储方法有关,即MessageQueue的enqueueMessage方法。。
- 无论是即时消息还是延迟消息,都是计算出具体的时间,然后作为消息的when字段进程赋值。
- 然后在MessageQueue中找到合适的位置(安排when小到大排列),并将消息插入到MessageQueue中。 这样,MessageQueue就是一个按照消息时间排列的一个链表结构。
9、Handler所发送的Delayed消息时间准确吗?
实际上,这个问题与线程安全性为同一个问题,多线程中线程一旦安全,时间就不能准确;时间一旦准确,线程就一定不安全。 所以,Handler所发送的Delayed消息时间基本准确,但不完全准确。 因为多个线程去访问这个队列的时候,在放入对列和取出消息的时候都会加锁,当第一个线程还没有访问完成的时候,第二个线程就无法使用,所以他实际的时间会被延迟。
10、怎么从消息队列中取消息然后执行?
由Looper的loop()方法循环从MessageQueue中取出Message对象(通过调用MessageQueue的next()方法), 调用msg.target获取到发送消息的Handler对象,然后再调用handler的dispatchMessage()方法将信息分发给对应handler执行。
java
Looper类
final MessageQueue mQueue;
public static void loop() {
final Looper me = myLooper();
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
try {
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
} finally {}
msg.recycleUnchecked();
}
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);//会阻塞的过程
synchronized (this) {
// Try to retrieve the next message. Return if found.
//取消息过程
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
}
}
11、为什么取消息也是用的死循环呢?
死循环就是为了保证一定要返回一条消息,如果没有可用消息,那么就阻塞在这里,一直到有新消息的到来。 其中,nativePollOnce方法就是阻塞方法,nextPollTimeoutMillis参数就是阻塞的时间。
那什么时候会阻塞呢?两种情况:
1、有消息,但是当前时间小于消息执行时间,
java
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX\_VALUE);
}
这时候阻塞时间就是消息时间减去当前时间,然后进入下一次循环,阻塞。
2、没有消息的时候,
java
if (msg != null) {}
else {
// No more messages.
nextPollTimeoutMillis = -1;
}
\-1就代表一直阻塞。
12、MessageQueue没有消息时候会怎样?阻塞之后怎么唤醒呢?说说pipe/epoll机制?
当消息不可用或者没有消息的时候就会阻塞在next方法,而阻塞的办法是通过pipe/epoll机制
epoll机制是一种IO多路复用的机制,具体逻辑就是一个进程可以监视多个描述符,当某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作,这个读写操作是阻塞的。在Android中,会创建一个Linux管道(Pipe)来处理阻塞和唤醒。 当消息队列为空,管道的读端等待管道中有新内容可读,就会通过epoll机制进入阻塞状态。 当有消息要处理,就会通过管道的写端写入内容,唤醒主线程。
13、子线程中维护的looper,消息队列无消息的时候处理方案是什么?有什么用,主线程呢?
消息队列无消息时候,此时Looper会一直阻塞状态。
处理方案:
-
在loop机制中有个quit和quitSafely函数,他们调用messageQueue中的quit函数,从而将消息队列中的全部的消息给全部remove掉,然后释放内存;
-
紧接着就会调运行到quit方法中最后一行nativeWake函数唤醒所有等待的地方。
-
醒来之后然后继续在阻塞的地方放下执行,发现msg==null,直接return null;这个时候looper结束,退出死循环,释放线程。
子线程创建 Looper 并执行 loop() 的线程在任务结束的时候,需要手动调用 quit。否则,线程将由于 loop() 的轮询一直处于可运行状态,CPU 资源无法释放。更有可能因为 Thread 作为 GC Root 持有超出生命周期的实例引发内存泄漏。当 quit 调用后,Looper 不再因为没有 Message 去等待,而是直接取到为 null 的 Message,这将触发轮循死循环的退出。
如果在子线程中创建了一个Handler,那么就必须做三个操作
java
1. prepare();
2. loop();
3. quit();
主线程中能否调用quit()方法?
是不能的。它会抛出一个异常,让程序挂掉。在内存不足的时候 App 由 AMS 直接回收进程
java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
}
源码分析:
java
Looper
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
MessageQueue
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//remove:将消息队列中的消息全部干掉,把消息全部干掉,也就释放了内存
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr); //叫醒所有等待的地方,醒了之后,继续往下执行。
}
}
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//就是在这里根据nextPollTimeoutMillis判断是否要阻塞
//native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
//阻塞操作,等待nextPollTimeoutMillis时长
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
if (msg != null) {
} else {
//没有消息,nextPollTimeoutMillis复位
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//如果消息队列正在处于退出状态返回null,调用dispose();释放该消息队列
if (mQuitting) {quit时候设置mQuitting = true;
dispose();
return null;
}
}
}
14、同步屏障和异步消息是怎么实现的?
其实在Handler机制中,有三种消息类型:
- 同步消息。也就是普通的消息。
- 异步消息。通过setAsynchronous(true)设置的消息。
- 同步屏障消息。通过postSyncBarrier方法添加的消息,特点是target为空,也就是没有对应的handler。
这三者之间的关系如何呢?
- 正常情况下,同步消息和异步消息都是正常被处理,也就是根据时间when来取消息,处理消息。
- 当遇到同步屏障消息的时候,就开始从消息队列里面去找异步消息,找到了再根据时间决定阻塞还是返回消息。
- 也就是说同步屏障消息不会被返回,他只是一个标志,一个工具,遇到它就代表要去先行处理异步消息了。
所以同步屏障和异步消息的存在的意义就在于有些消息需要"加急处理"。
15、同步屏障和异步消息有具体的使用场景吗?
使用场景就很多了,比如绘制方法scheduleTraversals。
java
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);
在该方法中加入了同步屏障,后续加入一个异步消息MSG_DO_SCHEDULE_CALLBACK,最后会执行到FrameDisplayEventReceiver,用于申请VSYNC信号。
16、消息是怎么被维护的?被谁维护的?
Message由Looper,MessageQueue进行维护;Looper是在ActivityThread中调用Looper.prepareMainLooper()中进行创建,创建的同时也创建好了MessageQueue。
java
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
17、Message消息被分发之后会怎么处理?消息怎么复用的?
- loop方法,在消息被分发之后,也就是执行了dispatchMessage方法之后,
- 还偷偷做了一个操作------recycleUnchecked。在recycleUnchecked方法中,释放了所有资源,然后将当前的空消息插入到sPool表头。这里的sPool就是一个消息对象池,它也是一个链表结构的消息,最大长度为50。
那么Message又是怎么复用的呢?
在Message的实例化方法obtain中:直接复用消息池sPool中的第一条消息,然后sPool指向下一个节点,消息池数量减一。
java
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();
}
Looper类
final MessageQueue mQueue;
public static void loop() {
final Looper me = myLooper();
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
try {
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
} finally {}
msg.recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
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++;
}
}
}
18、为什么Message对象通过Obtain获取?为什么使用复用池?为什么使用复用池就可以优化?
使用复用池,让内存使用更加高效,优化内存。使用复用池可以减少对象的创建过程,因为我们消息比较多,要创建多个对象,创建就要分配内存。如果对象太多的就要频繁的进行垃圾回收,我们消息一般处理的比较快,创建后很快就被执行完了,但是要进行垃圾回收,相对耗时,回收时候也可能造成内存抖动。
19、我们使用Message时应该如何创建他?
通过obtain进行创建,为什么要用复用池?让内存使用更加高效,优化内存, 为什么使用复用池就能优化,就能更高效?原因是什么?减少对象的创建过程?为什么?创建就要分配内存; 由于Message创建非常频繁,如果不断以new的方式去创建它,可能会导致垃圾回收机制中新生代被占满,从而触发GC,产生内存抖动。 所以在Android的Message机制里面,对Message的管理采用了享元设计模式,通过obtain进行创建,复用一个消息池中已经被回收的message。
- obtain()维持了一个Message的pool(池子)我们在构建一个消息的时候,一般的步骤是先obtain一个消息,然后对它的各个字段进行设置,像target、data、when、flags...
- 当MessageQueuez去释放消息的时候(quit),它只是把消息的内容置空了,然后再把这条处理的消息放到池子里面来,让池子不断变大。
- 在这个池子里面,最多放置50个消息。如果消息超过了50个消息,这个池子也不要了,然后mMessage也为空,则它也会被及时的回收。
主要的 Message 回收时机是:
- 在 MQ 中 remove Message 后,调用msg. recycleUnchecked
- 单次 loop 结束后,调用msg. recycleUnchecked.
- 我们主动调用 Message 的 recycle 方法,后从而调用 recycleUnchecked;
20、MessageQueue是如何保证线程安全的?
添加消息到队列和获取数据的方法都加了synchronized同步锁。
21、一个线程有几个Handler?
我们可以多次new一个Handler;所以一个线程可以有无数个handler。
一个线程可以拥有多 Handler,因为 Handler 最终是被 Message 持用的(post 里面的 Runnable 最终也会被包装成一个 Message),以便 Looper 在拿到 Message 后调用 Handler 的 dispatchMessage 完成回调,而且项目中仔细去看也确实如此,我们可以每个 Activity 中都创建一个 Handler 来处理回调到主线程的任务。
java
msg.target = this;(this就是当前handler)
looper类中
Handler target;
msg.target.dispatchMessage(msg);
22、为什么一个线程只能由一个Looper和MessageQueue?
因为加入我们有多个MesaageQueue,假如我们存一个延时2s消息到队列1,延时4s的消息到队列2,如果我们先从队列2中读取,则延时4s的消息先执行,不符合设计要求。
23、Looper是干嘛的?怎么获取当前线程的Looper?一个线程有几个Looper?如何保证?
23.1 looper干嘛的?
在Handler发送消息之后,消息就被存储到MessageQueue中,而Looper就是一个管理消息队列的角色。Looper会从MessageQueue中不断的查找消息,也就是loop方法,并将消息交回给Handler进行处理。
23.2 Looper的获取?
就是通过ThreadLocal机制:
通过prepare方法创建Looper并且加入到sThreadLocal中,通过myLooper方法从sThreadLocal中获取Looper。
java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
23.3 一个线程只有一个Looper。
首先要了解线程thread和Looper的关系? 主线程Looper哪里初始化?ActivityThread zygote为应用分配虚拟机,ActivityThread的main函数的的触发是由 AMS触发,在main函数中做了以下两件事
java
Looper.prepareMainLooper();
Looper.loop();
生命周期管理,页面响应、点击时间事件,按键事件等等都会变成MSG,通过looper管理。 main函数中的这两件事,为进程内的MSG管理提供初始化。
java
Looper.prepareMainLooper();//初始化looper,
Looper.loop();//需要Looper.loop()启动,后执行。里面又个for(;;)死循环,一直循环取消息
23.4 为什么么只有一个looper?
答:Looper.prepareMainLooper()调用时候,调用prepare()函数,通过ThreadLocal来保证,它通过static和final修饰,只能初始化一次,保证线程唯一ThreadLocal。
- 首先要通过ThreadLocal的get方法获取当前线程所对应的ThreadLocalmap中的key==ThreadLocal的value值。
- 如果value不为null,也就是looper不为空,就抛出一个运行时异常,提示只能有一个looper。
- 如果map为空的话,就调用ThreadLocal的set函数,将该ThreadLocal作为key,对应的looper作为value存储在thread中的ThreadLocalmap。
只要线程不变,ThreadLocal对象也不变,key也就不变了,然后通过throw new RuntimeException("Only one Looper may be created per thread")保证只能有一个value,Map中也就无法插入第二个键值对,因此保证了一个线程只有一个。
java
Looper.prepareMainLooper();------------>
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));
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
23.5 可以多次创建Looper吗?
Looper的创建是通过Looper.prepare方法实现的,而在prepare方法中就判断了,当前线程是否存在Looper对象,如果有,就会直接抛出异常。所以同一个线程,只能创建一个Looper,多次创建会报错。
24、Looper中的传入的quitAllowed字段是啥?有什么用?
QuitAllowed判断是否允许退出,如果这个字段为false,代表不允许退出,就会报错。 Looper中的quit方法就是退出消息队列,终止消息循环。调用到MessageQueue中的quit时,判断quitAllowed字段。
- 首先设置了mQuitting字段为true。
- 然后判断是否安全退出,如果安全退出,就执行removeAllFutureMessagesLocked方法,它内部的逻辑是清空所有的延迟消息,之前没处理的非延迟消息还是需要取处理,然后设置非延迟消息的下一个节点为空(p.next=null)。
- 如果不是安全退出,就执行removeAllMessagesLocked方法,直接清空所有的消息,然后设置消息队列指向空(mMessages = null)
当调用quit方法之后,
- 消息的发送:当调用了quit方法之后,mQuitting为true,消息就发不出去了,会报错。
- 消息的处理,loop和next方法:当mQuitting为true的时候,next方法返回null,那么loop方法中就会退出死循环。
那么这个quit方法一般是什么时候使用呢?
主线程中,一般情况下肯定不能退出,因为退出后主线程就停止了。所以是当APP需要退出的时候,就会调用quit方法,涉及到的消息是EXIT_APPLICATION,大家可以搜索下。
子线程中,如果消息都处理完了,就需要调用quit方法停止消息循环。
java
Looper
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
MessageQueue
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//remove:将消息队列中的消息全部干掉,把消息全部干掉,也就释放了内存
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr); //叫醒所有等待的地方,醒了之后,继续往下执行。
}
}
发送消息
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//读取消息
Message next() {
synchronized (this) {
// Process the quit message now that all pending messages have been handled.
//如果消息队列正在处于退出状态返回null,调用dispose();释放该消息队列
if (mQuitting) {//quit时候设置mQuitting = true;
dispose();
return null;
}
}
}
25、Handler机制内存泄漏原因?为什么其他的内部类没有说过这个问题?
主线程 ---> threadlocal ---> Looper ---> MessageQueue ---> Message ---> Handler ---> Activity
1、handler创建时候就对持有当前Activity得引用,同时message持有对handler的引用。MessageQueue持有Message;Message持有了Handler;handler是匿名内部类,持有this Activity,Activity持有大量内存,就会造成内存泄漏。
2、 发送一个延迟消息时候,消息被处理前,该消息一直保存在消息队列中,在持续保存这段时间,messageque持有对message的引用,Message持有了handler;在我们的activity中建立一个Handler的实例,该handler实例默认持有了外部的对象acticity的引用,当我们调用acticity的ondestroy方法时,activity销毁了,但是根据可达性分析,我们的需要的activity存在被handler引用,只要handler不被释放,没办法会销毁,就造成了内存泄漏。
解决该办法的两个方法:
1、将handler变为static,就会不会引用activity,因为静态内部类不会持有外部的类的引用。 2、使用弱引用weakreference持有activity;垃圾回收器在回收过程中,不过内存空间够与否,都将弱引用对象进行回收。 3、在外部结束生命周期的ondestroy方法中,清除消息队列中的消息;只要清除该msg,引用就会断开,避免内存泄漏。调用这个方法mHandler.removeCallbacksAndMessages(null)
为什么其他内部类没有这个说法?
因为正常来说其他内部类持有外部类的对象,但是在内部持有外部类的时候没有做耗时操作,也就不存在这种持续引用的,造成内存泄漏。
26、Message是怎么找到它所属的Handler然后进行分发的?
在loop方法中,找到要处理的Message,然后调用了这么一句代码处理消息:
msg.target.dispatchMessage(msg);
所以是将消息交给了msg.target来处理,那么这个target是啥呢?Hanlder发送消息的时候,会设置msg.target = this,所以target就是当初把消息加到消息队列的那个Handler。
java
//Handler
private boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis) {
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
27、Handler 的 post(Runnable) 与 sendMessage 有什么区别
Hanlder中主要的发送消息可以分为两种:
java
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;
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
post方法给Message设置了一个callback。
那么这个callback有什么用呢?我们再转到消息处理的方法dispatchMessage中看看:
这段代码可以分为三部分看:
- 如果msg.callback不为空,也就是通过post方法发送消息的时候,会把消息交给这个msg.callback进行处理,然后就没有后续了。
- 如果msg.callback为空,也就是通过sendMessage发送消息的时候,会判断Handler当前的mCallback是否为空,
- 如果不为空就交给Handler.Callback.handleMessage处理。
- 如果mCallback.handleMessage返回true,则无后续了。
- 如果mCallback.handleMessage返回false,则调用handler类重写的handleMessage方法。
- 如果不为空就交给Handler.Callback.handleMessage处理。
所以post(Runnable) 与 sendMessage的区别就在于后续消息的处理方式,是交给msg.callback还是 Handler.Callback或者重写的Handler.handleMessage。
java
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();
}
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
public void handleMessage(@NonNull Message msg) {
}
28、Handler.Callback.handleMessage 和 Handler.handleMessage 有什么不一样?为什么这么设计?
接着上面的代码说,这两个处理方法的区别在于Handler.Callback.handleMessage方法是否返回true: 如果为true,则不再执行Handler.handleMessage
如果为false,则两个方法都要执行。
那么什么时候有Callback,什么时候没有呢?这涉及到两种Hanlder的 创建方式:
java
val handler1= object : Handler(){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
}
}
val handler2 = Handler(object : Handler.Callback {
override fun handleMessage(msg: Message): Boolean {
return true
}
})
常用的方法就是第1种,派生一个Handler的子类并重写handleMessage方法。而第2种就是系统给我们提供了一种不需要派生子类的使用方法,只需要传入一个Callback即可。
29、handler.post方法,它和常用的handler.sendmessage方法的区别是什么?
1、先看用法1之主线程中使用:
java
new Handler().post(new Runnable() {
@Override
public void run() {
mTest.setText("post");//更新UI
}
});
new了Runnable像是开启了一个子线程,但是不然,这儿调用的是run方法,而不是start方法,因此并不是子线程,其实还是在主线程中;
那为什么又要使用post方法呢?其实一般不这样用,也没人这样用,并没有多大意义,这就像是在主线程中给主线程sendmessage,并没有什么意义(我们知道sendmessage是子线程为了通知主线程更新UI的),主线程是可以直接更新UI的。
2、 再看用法2之子线程中使用:
java
Handler handler;
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
handler.post(new Runnable() {
@Override
public void run() {
mTest.setText("post");//更新UI
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
这儿的post并不是新开启的子线程,存在的子线程只有一个,即为new的Thread,那么为什么我们在其中可以setText做更新UI的操作呢? 其实post方法post过去的是一段代码,相当于将这个Runable体放入消息队列中,那么looper拿取的即为这段代码去交给handler来处理,其实也相当于我们常用的下面这段代码:
java
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
mTest.setText("handleMessage");//更新UI
break;
}
}
};
用这个Runnable体代替了上面这一大段代码,当然,我们的post方法就可以执行UI操作了。
平常情况下我们一个activity有好多个子线程,那么我们都会采用上面这种handleMessage(msg)方式,然后case 0:case 1:等等,但是当我们只有一个子线程时呢,用post反而比上面一大串代码轻便了不少,何乐而不为呢?
30、为什么主线程可以new Handler?子线程new handler需要做什么工作。
因为在应用启动时候就启动了loop,所以主线程可以new handler。
- 主线程在ActivityThread.java里有一个main()函数,它是Android每一个应用最早执行的函数。prepare是初始化,初始化了一个Looper。
- 然后又接着调用的Looper.loop()函数,这个loop()函数其实就是执行了那个for循环,它不断的调用next()函数,通过调用next()函数去轮询我们的MessageQueue。
- 如果不调用prepare(),Looper没有被初始化;如果不调用loop(),Looper的机制滚动不起来。所以,所有的执行必须先prepare(),然后再loop()。
ActivityThread中做了哪些关于Handler的工作?(为什么主线程不需要单独创建Looper) 主要做了两件事:
1、在main方法中,创建了主线程的Looper和MessageQueue,并且调用loop方法开启了主线程的消息循环。
java
public static void main(String\[] args) {
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2、创建了一个Handler来进行四大组件的启动停止等事件处理
java
final H mH = new H();
class H extends Handler {
public static final int BIND\_APPLICATION = 110;
public static final int EXIT\_APPLICATION = 111;
public static final int RECEIVER = 113;
public static final int CREATE\_SERVICE = 114;
public static final int STOP\_SERVICE = 116;
public static final int BIND\_SERVICE = 121;
在子线程中new Handler需要做哪些准备?
如果子线程中想要进行Handler操作,就必须在子线程中执行prepare() 和 loop()。
java
子线程
Thread thread = new Thread() {
Looper looper;
@Override
public void run() {
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
public Looper getLooper() {
return looper;
}
};
thread.start();
//错误的做法
Handler handler = new Handler(thread.getLooper()); //创建handler。要必须传入looper。
//但是主线程和子线程执行不同步不一定能拿到looper,
//无法保证或缺的looper不为null,所以是错误的
//正确做法利用创建子线程
HandlerThread handler = new HandlerThread();
线程创建时候,prepare在run方法,handler在主线程中创建的的,不能保证prepare执行之后再执行handler的创建。
- 解决办法可以让子线程sleep或者wait,等一下。
- 真正的方法是利用HandlerThread解决同步问题。
- 利用synchronized实现的。synchronized是一个内置锁,内置到JVM中,开锁关锁是由JVM内部完成的。
31、HandlerThread源码分析及使用
使用实例:
java
public class HandlerThreadActivity extends BaseActivity {
private static final String TAG = "HandlerThreadActivity";
private Button mStartBtn;
private Handler mHandler;
private HandlerThread mHandlerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity\_handler\_thread);
mStartBtn = findViewById(R.id.start\_btn);
//子线程
mHandlerThread = new HandlerThread("THREAD\_NAME");
mHandlerThread.start();
//主线程
mHandler = new Handler(mHandlerThread.getLooper());
mStartBtn.setOnClickListener(new View\.OnClickListener() {
@Override
public void onClick(View v) {
//主线程发送耗时消息给子线程
mHandler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getId() + " " + String.valueOf((Looper.myLooper()
== Looper.getMainLooper())) + " 任务:" + this.hashCode());
SystemClock.sleep(3000);
}
});
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
}
}
HandlerThread源码分析
- HandlerThread 运行 start() 方法,回调 run() 方法。
- 在 run() 方法中通过 Looper.prepare() 来创建消息队列,并通过 Looper.looper() 方法来开启消息循环。
- 由于 Loop.loop() 是一个死循环,导致 run() 也是无线循环,因此当我们不需要使用 HandlerThread 的时候,要调用它的 quit() 方法或者 quiteSafely() 方法。
java
HandlerThread
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
//code后续代码
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {//
try {
wait();//此时释放锁,等待。
//code
} catch (InterruptedException e) {
wasInterrupted = true;
}
}
}
加入锁的机制保证mLooper = Looper.myLooper(),先于getlooper执行,同时加入wait也保证了mylooper先执行。
执行Looper的prepare()方法,生成Looper对象,而是把Looper对象和当前线程对象形成键值对(线程为键),存放在ThreadLocal当中,然后生成handler对象,调用Looper的myLooper()方法,得到与Handler所对应的Looper对象,这样的话,handler、looper 、消息队列就形成了一一对应的关系
- Wait:等待;释放锁,线程可以处理其他业务。
- sleep:线程等待,线程不会做其他事情。
一个线程可能会有多个handler,多个地方获取looper,所以要notifyall。唤醒所有handler。但是notifyAll之后不会立即执行,其他的要代码要处在就绪状态,先执行完synchronized里面的所有代码,才会执行wait之后的代码。
- notify:唤醒对应线程
- notifyAll:唤醒所有线程。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
总结: A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。 C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
子线程向主线程发送消息:
主线程中定义handler,接受消息并处理
java
private static Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case UPDATETEXT:
Log.e("handler","做ui线程中的事");
break;
default:
break;
}
}
};
子线程中发送消息:处理完耗时任务操作时发送消息给主线程,更新UI。
java
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "";
message.what = UPDATETEXT;
mHandler.sendMessage(message);
}
}).start();
主线程向子线程发送消息:
主线程碰到耗时操作要子线程完成,让子线程执行指定的操作,利用handler创建子线程,自动创建looper。
项目中的一段代码:传感器注册时比较耗时,交给子线程处理。
java
private Handler mRegisterThreadHandler;
private ApplicationLifeObserver() {
HandlerThread mThread = new HandlerThread("register-sensor");
mThread.start();
/**
* 这里要将HandlerThread创建的looper传递给threadHandler,即完成绑定;
*/
mRegisterThreadHandler = new Handler(mThread.getLooper()) {
@Override
public void handleMessage(Message msg) {//这儿可做主线程耗时的操作;
if (msg.what == REGISTER\_SENSOR) {
registerSensorListeners();
}
if (msg.what == UN\_REGISTER\_SENSOR) {
unregisterSensorListeners();
}
}
};
}
32、既然可以存在多个handler往MessageQueue中添加数据(发送消息时各个handler可能处于不同线程),那么他内部是如何保证线程安全的?取消息呢?
handler往messageQueue中添加消息是通过synchronized保证线程安全。在发送消息时候,如队列的方法中加入synchronized锁。取消息也是通过synchronized锁保证安全的。
通过synchronized来保证线程安全的。 发送消息时候,入队列的方法equeueMessage加synchronized锁。
java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
.........
}
}
取消息时
looper.loop中实现for(;;){loopOnce}--->oopOnce:
1⃣Message msg =me.mQueue.next(); // might block;2⃣️msg.target.dispatchMessage(msg);------>handler.dispatchMessage------>handler.handleMessage。
其中取消息MessageQueue中的next方法实现加入synchronized。
java
Message msg = me.mQueue.next(); // might block
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
}
}
33、IntentService是啥?有什么使用场景?
java
public abstract class IntentService extends Service {
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService\[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
- 首先,这是一个Service,并且内部维护了一个HandlerThread,也就是有完整的Looper在运行。
- 还维护了一个子线程的ServiceHandler。
- 启动Service后,会通过Handler执行onHandleIntent方法。
- 完成任务后,会自动执行stopSelf停止当前Service。
- 所以,这就是一个可以在子线程进行耗时任务,并且在任务执行后自动停止的Service。
34、BlockCanary使用过吗?说说原理
BlockCanary是一个用来检测应用卡顿耗时的三方库。
View的绘制也是通过Handler来执行的,所以如果能知道每次Handler处理消息的时间,就能知道每次绘制的耗时了?那Handler消息的处理时间怎么获取呢?
再去loop方法中找找细节:
java
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,然后统计两次打印日志的时间不就相当于处理消息的时间了?
java
Looper.getMainLooper().setMessageLogging(mainLooperPrinter);
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
这就是BlockCanary的原理。
35、IdleHandler是啥?有什么使用场景?
当MessageQueue没有消息的时候,就会阻塞在next方法中,其实在阻塞之前,MessageQueue还会做一件事,就是检查是否存在IdleHandler,如果有,就会去执行它的queueIdle方法。
java
private IdleHandler\[] mPendingIdleHandlers;
Message next() {
int pendingIdleHandlerCount = -1;
for (;;) {
synchronized (this) {
//当消息执行完毕,就设置pendingIdleHandlerCount
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//初始化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是怎么加进去的(IdleHandler就是当消息队列里面没有当前要处理的消息了,需要堵塞之前,可以做一些空闲任务的处理。)
java
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
//做事情
return false;
}
});
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
常见的使用场景有:启动优化。
我们一般会把一些事件(比如界面view的绘制、赋值)放到onCreate方法或者onResume方法中。但是这两个方法其实都是在界面绘制之前调用的,也就是说一定程度上这两个方法的耗时会影响到启动时间。
所以我们可以把一些操作放到IdleHandler中,也就是界面绘制完成之后才去调用,这样就能减少启动时间了。
但是,这里需要注意下可能会有坑。
如果使用不当,IdleHandler会一直不执行,比如在View的onDraw方法里面无限制的直接或者间接调用View的invalidate方法。
其原因就在于onDraw方法中执行invalidate,会添加一个同步屏障消息,在等到异步消息之前,会阻塞在next方法,而等到FrameDisplayEventReceiver异步任务之后又会执行onDraw方法,从而无限循环。
36、handler的消息阻塞是怎么实现的,Looper.loop方法是死循环,为什么不会卡死(ANR)?
应用卡死,也就是ANR产生:
- 5s之内没有响应输入的事件,比如按键,触摸屏幕等等;
- 广播接收器在10秒之内没有执行完毕。
ANR产生的机制:定时器;
但是input按键事件产生机制:下一个按键事件,下一个按键事件来的时候发现上一个事件6s内完成了就不anr,没有完成就anr
AMS管理机制
- 每一个应用都存在于自己的虚拟机中,也就是每一个应用都有自己的一个main函数。
- 启动流程:launcher-----zygote------application------activityThread------main()
- 所有应用所在的生命周期(包括activity,service的所有生命周期)都在这个looper里面,而且,他们都是以消息的方式存在的。
阻塞后怎么唤醒?
唤醒线程的方法:
1、loop中添加message,通过nativeWait----loop运作
2、输入事件
Looper.loop方法是死循环,为什么不会卡死(ANR)?
- 主线程本身就是需要一直运行的,因为要处理各个View,界面变化。所以需要这个死循环来保证主线程一直执行下去,不会被退出。
- 真正会卡死的操作是在某个消息处理的时候操作时间过长,导致掉帧、ANR,而不是loop方法本身。应用卡死ANR压根和这个looper没有关系,应用在没有消息需要处理的时候,他是在睡眠,释放线程了;卡死是ANR,而Looper是睡眠。
- 主线程里面的任何事件都是主线程的message,如果没有message,说明没有事件要处理了,就要sleep,cpu省下来,所以阻塞时候没有埋炸弹定时器,所以不会阻塞。
- 当没有消息的时候,会阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生。所以死循环也不会特别消耗CPU资源。
java
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
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) {
// 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) {
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
}