文章目录
- 前言
- 一、题目
- 1.Handler Looper MessageQueue之间的关系是什么
- 2.为何可以在主线程中new Handler,如果要在子线程中new Handler需要做什么工作?
- 3.子线程中维护的Looper,当消息队列无消息的时候,处理方案是什么
- 4.既然可以存在多个handler 往mq中add 数据,那内部是如何确保线程数据安全的?
- 5.一个线程可以有几个handler
- 6.一个线程可以有几个looper
- 7.handler的内存泄漏的原因?为什么其他的内部类没有提说过有这个问题?
- 8.使用Handler的postDelay后消息队列会有什么变化
- 8.looper死循环为什么不会导致应用卡死?
- 9.Handler消息遇到紧急消息如何处理(消息机制的同步屏障)
- 总结
前言
如果没有进行过Handler源码相关阅读和整理,强烈建议先看笔者的这篇博客
Handler消息机制整理
这篇记录Handler相关面试题的学习和整理
一、题目
1.Handler Looper MessageQueue之间的关系是什么
见这篇博客
Handler消息机制整理
2.为何可以在主线程中new Handler,如果要在子线程中new Handler需要做什么工作?
子线程中使用Handler必须使用Looper.prepare() &&Looper.Loop()方法去开启Loop
主线程中一启动,就已经了进行了Looper.prepareMainLooper() &&Looper.Loop()方法相关工作
3.子线程中维护的Looper,当消息队列无消息的时候,处理方案是什么
当消息队列无消息时,应该调用Looper.quitSafe()或者Looper.quit()方法,清空消息队列中的消息,而后清空message的target callback data等内容,可以释放内存,避免内存泄漏
如果不处理,直接阻塞了,具体原因看上面那篇博客
//Message.java
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 = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
4.既然可以存在多个handler 往mq中add 数据,那内部是如何确保线程数据安全的?
//MessageQueue.java
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) {
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;
}
...
上了synchronized的锁
5.一个线程可以有几个handler
如果使用过handler的同学都知道在实际使用过程中可以有无数个handler。
6.一个线程可以有几个looper
只能有一个,每次Looper.prepare会采用类似键值对的形式往threadholder里面扔一个线程和其对应的Looper进去
7.handler的内存泄漏的原因?为什么其他的内部类没有提说过有这个问题?
在调用到Message的enqueueMessage的是否会将 handler赋值给 message的target,而Message 在 messagequeue中等待的时候如果生命周期发生变化但是message中的target不释放就会导致泄漏,毕竟内存泄漏就是生命周期不相等导致的
8.使用Handler的postDelay后消息队列会有什么变化
//Handler.java
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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的之前都是传入了一个时间值,而在messagequeue的源码中,message也是按照时间顺序排列的 一个队列。在插入之后会查看是否需要wake。如果我们插入的是一个time为0的时候messagequeue会立刻唤醒,如果插入的是一个延迟任务,将不会唤醒messagequeue。
8.looper死循环为什么不会导致应用卡死?
looper的死循环的机制本质是一个for循环,并不是因为消息没有处理导致的死循环
ANR产生的原因是因为将一个事件封装成Msg之后发送出去,计算这个Msg处理事件的回调是否超过五秒,如果超过五秒,就调用Handler进行一个ANR提醒
9.Handler消息遇到紧急消息如何处理(消息机制的同步屏障)
关联问题:同步屏障问题,涉及源码如下:
在Message的next()方法中
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());
}
- 开启同步屏障的方法:往消息队列中添加一个msg.target==null的消息
- 而后Message的next()会一直往后遍历,优先处理异步的消息(msg.isAsynchronous()==true)
- 而后移除消息屏障
相关源码:viewRootImple
总结
祝自己上岸