关于Handler,看看面试官都问了我哪些?这些你都知道吗?

}

面试官:为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
小王:
每一个handler必须要对应一个looper,主线程会自动创建Looper对象,不需要我们手动创建,所以主线程可以直接创建handler。
在new handler的时候没有传入指定的looper就会默认绑定当前创建handler的线程的looper,如果没有looper就报错。

因为在主线程中,Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。而对于子线程而言,没有任何对象帮助我们维护Looper对象,所以需要我们自己手动维护。
所以要在子线程开启Handler要先创建Looper,并开启Looper循环

如果在子线程中创建了一个Handler,那么就必须做三个操作:

  1. prepare();
  2. loop();
  3. quit();

面试官:子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
小王:
在Handler机制里面有一个Looper,在Looper机制里面有一个函数,叫做quitSafely()和quit()函数,这两个函数是调用的MessageQueue的quit()。

/**

  • Quits the looper.
  • Causes the {@link #loop} method to terminate without processing any
  • more messages in the message queue.
  • Any attempt to post messages to the queue after the looper is asked to quit will fail.
  • For example, the {@link Handler#sendMessage(Message)} method will return false.
  • Using this method may be unsafe because some messages may not be delivered
  • before the looper terminates. Consider using {@link #quitSafely} instead to ensure
  • that all pending work is completed in an orderly manner.
  • @see #quitSafely
    */
    public void quit() {
    mQueue.quit(false);
    }

/**

  • Quits the looper safely.
  • Causes the {@link #loop} method to terminate as soon as all remaining messages
  • in the message queue that are already due to be delivered have been handled.
  • However pending delayed messages with due times in the future will not be
  • delivered before the loop terminates.
  • Any attempt to post messages to the queue after the looper is asked to quit will fail.
  • For example, the {@link Handler#sendMessage(Message)} method will return false.

*/
public void quitSafely() {
mQueue.quit(true);
}

再进入到MessageQueue的quit()函数。

void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException(“Main thread not allowed to quit.”);
}

synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;

if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}

// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}

它会remove消息,把消息队列中的全部消息给干掉。
把消息全部干掉,也就释放了内存

private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;😉 {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}

而在quit()函数的最后一行,有一个nativeWake()函数。

// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);

这个函数的调用,就会叫醒等待的地方,醒来之后,就接着往下执行。

//native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
//阻塞操作,等待nextPollTimeoutMillis时长
nativePollOnce(ptr, nextPollTimeoutMillis);

往下执行后,发现 Message msg = mMessages; 是空的,然后就执行了这个,就接着往下走。

if (msg != null) {

} else {
// No more messages.
//没有消息,nextPollTimeoutMillis复位
nextPollTimeoutMillis = -1;
}

然后又调用了这个方法,并且return了null。

// Process the quit message now that all pending messages have been handled.
//如果消息队列正在处于退出状态返回null,调用dispose();释放该消息队列
if (mQuitting) {
dispose();
return null;
}

所以说,这个时候Looper就结束了(跳出了死循环),则达成了第二个作用:释放线程

面试官:既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
小王:
这里主要关注 MessageQueue 的消息存取即可,看源码内部的话,在往消息队列里面存储消息时,会拿当前的 MessageQueue 对象作为锁对象,这样通过加锁就可以确保操作的原子性和可见性了。

消息的读取也是同理,也会拿当前的 MessageQueue 对象作为锁对象,来保证多线程读写的一个安全性。

面试官:我们使用 Message 时应该如何创建它?
小王:
创建的它的方式有两种:
一种是直接 new 一个 Message 对象,
另一种是通过调用 Message.obtain() 的方式去复用一个已经被回收的 Message,
当然日常使用者是推荐使用后者来拿到一个 Message,因为不断的去创建新对象的话,可能会导致垃圾回收区域中新生代被占满,从而触发 GC。

Message 中的 sPool 就是用来存放被回收的 Message,当我们调用 obtain 后,会先查看是否有可复用的对象,如果真的没有才会去创建一个新的 Message 对象。

补充:主要的 Message 回收时机是:
1.在 MQ 中 remove Message 后;
2.单次 loop 结束后;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

感谢您的阅读,在文末给大家准备一个福利。本人从事Android开发已经有十余年,算是一名资深的移动开发架构师了吧。根据我的观察发现,对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

所以在此将我十年载,从萌新小白一步步成长为Android移动开发架构师的学习笔记,从Android四大组件到手写实现一个架构设计,我都有一一的对应笔记为你讲解。

当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。这也是我这些年来养成的习惯,一定要学会把好的东西,归纳整理,然后系统的消化吸收,这样才能极大的提高学习效率和成长进阶。碎片、零散化的东西,我觉得最没有价值的。就好比你给我一张扑克牌,我只会觉得它是一张废纸,但如果你给我一副扑克牌,它便有了它的价值。这和我们收集资料就要收集那些系统化的,是一个道理。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

统化的,是一个道理。

[外链图片转存中…(img-DXdnsjT8-1712377817170)]

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值