- 详解Android Handler 机制 (一)用法全解
- 详解Android Handler 机制 (二)源码分析
- Android-Handler源码解析-Handler
- Handler、Looper、MessageQueue源码解析—
- Handler、Looper、MessageQueue源码解析—
- Handler、Looper、MessageQueue源码解析—
- Handler、Looper、MessageQueue源码解析—
- Android进阶:三、这一次,我们用最详细的方式解析Andro
- Android进阶:三、这一次,我们用最详细的方式解析Andro
- Android-Handler源码解析-MessageQueue
前言
上一篇handler源码解析一我们跟踪了handler的初始化和发送消息机制,这一篇我们跟踪处理消息机制。
我们知道Looper.prepare()来给线程创建一个消息队列,Looper.loop()来取出消息队列中的消息。
一、 小知识点
native void nativePollOnce(long ptr, int timeoutMillis)
timeoutMillis :0 ,代表不阻塞立即返回,负数代表一直阻塞直到添加新消息为止,正数代表最多等待多少时间。
二、Looper.loop()
我们简化一下loop方法的代码,这里我们只分析正常读取消息情况,也就是取出然后处理,
final Looper me = myLooper(); // 或得当前线程的looper
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 可能阻塞
if (msg == null) { // 没有消息表示消息队列正在退出
return; //for循环中return相当于break
}
msg.target.dispatchMessage(msg); // 处理消息
}
我们先进入取出方法queue.next,
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) { //一个死循环
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
// 当第一个节点的消息时间还没到,设置阻塞的等待时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 无需等待 ,返回头节点
mBlocked = false;
mMessages = msg.next;
msg.next = null;
msg.markInUse();
return msg;
} else {
// No more messages.
nextPollTimeoutMillis = -1; // 没有消息,等待唤醒
}
}
}
msg.recycleUnchecked(); //回收message,并加入回收链表中
}
那什么时候被唤醒呢?还记得messagequeue的enqueueMessage()么?里面调用了 nativeWake()方法,这个就是唤醒啦。
下面,我们来看dispatchMessage(), msg.target是当前handler,所以我们进入handler.dispatchMessage方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这个很简单,就不讲啦。
Looper的quit和quitSafely方法
非安全退出是清空所有消息队列中的消息,安全退出是只清空晚于当前时间的延迟消息。源码就不贴啦。
看完源码,面试一下吧!
-
一个线程有几个handler?一个线程有几个Looper和MessageQueue?如何保证?
多个handler,一个线程只有一个Looper和MessageQueue。Looper.prepare只能执行一次,否则报错,messageQueue是looper的一个变量,looper只有一个,messageQueue也只有一个。 -
什么是内存泄漏?Handler内存泄漏原因?如何避免?
内存泄漏:当一个对象本该被回收时, 另一个正在使用的对象持有它的引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了
原因:匿名内部类持有外部类,也就是activity,当调用postDelayed时,activity还没在时间到达之前被销毁了,会导致activity被持有引用而无法回收
解决方案: 1>. 使用静态内部类与弱引用,将handler设置为静态的,弱引用activity。2>. 在Activity的ondestrory中调用handler的removeCallbacksAndMessages(), 移除所有的消息和回调就意味着这个Handler被打回原型,也就可以回收了。 -
为何主线程可以直接new Handler()?子线程怎么new Handler?
主线程在APP启动过程中就调用了Looper.prepare和Looper.loop方法,而子线程需要手动调用这两个方法。 -
子线程中维护的Looper,消息队列中无消息的时候处理方案是什么?有何作用?
没有消息的时候,会调用nativepollonce阻塞,直至下一个消息到达,作用是释放CPU资源进入休眠状态,直到下个消息到达。 -
不同线程的多个handler往MessageQueue中添加数据,如何保证线程安全
通过synchronized messageQueue对象来保证了线程的安全性 -
使用Message如何创建它?
Message.obtain()方法,使用了享元设计模式。如果message被remove调,它会成为回收链表的一个节点,当需要新的消息的时候,会从这个队列中读取,如果该链表为空,则创建新的message。 -
Looper死循环为何不会导致应用卡死?
ANR出现的情况:1.在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)2.BroadcastReceiver在10秒内没有执行完毕3.service是20。在这些场景会发出ANR警告,但looper循环就是为了取消息去处理的,如果死循环退出了,也就不能处理各类事件了。 -
postMessageDelayed怎么延迟时间的?和postDelayed有没有什么区别?
postMessageDelayed会将 system.uptimeMillis+delay 赋值给message的when成员变量,messageQueue会根据message的when 插入到messageQueue中,和postDelayed没有区别,最终都是调用的postmessageAtTime方法。 -
Toast在子线程中如何使用
Toast需要借助Looper的,因为Toast也有new Handler哦,所以要像子线程中初始化handler一样,需要在前后加上Looper.prepare(),在之后加上Looper.Loop()。 -
两个子线程怎么使用handler进行交互?
这个可以参考HandlerThread哦
网友评论