美文网首页
Handler机制面试中的坑

Handler机制面试中的坑

作者: BlueSocks | 来源:发表于2023-05-30 20:24 被阅读0次

    这个是我最常用的android端面试题。首先它是整个android系统运作的基础,其次是大多数app开发都会使用到/接触过,最后该问题扩展性非常强,小到简单使用,framework技巧,大到黑科技,无声明activity启动都用涉及到。一般情况下面试是这个样子

    能请你聊一下android的handler机制吗?

    大部分回答会是这么个样子:handler机制涉及handler,messagequeue、message、looper四个部分,handler负责将消息message投递到messagequeue里面,looper是一个死循环,从messagequeue里面取出消息进行处理。稍微细节一点点的同学会加上looper会将获取到的消息dispatch给对应的handler进行处理。看过源码的同学会装一下,和面试官掰扯一下源代码,函数调用过程,looper.prepare,messagequeue.next,nativepollonce,大概如此

    上面的回答是散乱的,比较小白的,基本上判断为面经八股文,死记硬背。

    • 需要体现你的思考过程,你要为这个事情定基调。譬如:

    handler机制是android系统运行的基础,它采用生产者,消费者模式进行设计。其中生产者是handler,多个handler会生产消息message投递到线程共享的messagequeue有序单链表里面,再由线程共享looper进行消费,将message消息dispatch到其指定的handler进行处理。

    • 需要体现你的面广,你需要说明android系统都怎么基于handler机制运作。

    无论是activity/service/fragment的生命周期都基于handler机制运作,ui视图刷新/动画系统播放也是通过handler进行同步刷新,手机屏幕的触摸事件也是基于handler机制进行响应分发。

    第一阶段已经将handler机制表述得非常清楚。

    这里有几个细节要注意一下:

    1. handler的设计模式是生产者消费者(可以进一步问message是什么设计模式)
    2. messagequeue/looper是线程唯一共享的(可以进一步问ThreadLocal的原理)
    3. messagequeue是一个有序的单链表结构(可以进一步问怎么实现高效查询)

    1、looper.loop是个死循环,messagequeue.next也是个死循环,如果messagequeue取不到消息的时候,会发生什么情况,cpu会不会跑满100%?

    绝大多数同学对这段代码没有细抠,而是简单回复因为nativepollonce采用了了epoll机制,当没有取到消息的时候会挂起线程让渡cpu,等下一次消息到达的时候重新唤醒线程继续执行,所以cpu不会跑满100%。真实情况确实如此吗?我们来梳理一下,从messagequeue中取不到消息不代表里面没有消息,再尔如果当前主线程挂起让渡cpu,在下次消息到达唤醒线程,怎么确保线程能竞争到cpu资源? 有同学回复是因为前台线程优先级高。首先app还真不一定处于前台,其次前台线程也不仅仅是一个,android是多任务调度的系统,会有多个前台进程,这是第一个坑点。第二个坑点就是,messagequeue里面有一个闲时处理器,idlehandler的机制,当从messagequeue取不到消息的时候会执行idlerhandler(如有),一个线程不可能又是挂起,又是执行的,所以上面的回答太过想当然了。

    真实的情况是如何的呢?

    1. 首先不会让渡cpu,这样才有往下执行idlehandler的基础。如果从messagequeue中取不到消息,会继续往下执行判断是否有idlehandler,如果有就从里面获取最多5个idlehandle并执行,设置循环等待标识0,继续尝试获取消息;如果没有任何idlehandle就设置循环等待标识-1,自循环等待消息。
    2. 其次nativepollonce会阻塞是真的,不过有超时条件,也就是上面提到的循环等待标识设置,0标识查询没有可用(ready)的message就立即返回,-1表示等待直到有可用消息才返回,其他表示超时等待时间,一般是下一个消息when减去当前now时间,超时即返回。
    3. 最后epoll是基于linux内核工作,多路复用体现在1个内核线程监控多个fd状态,epoll比较高效是因为被监控fd状态ready了才会被查询到,从而得到处理。

    2、有没有使用过handler?处理什么问题?你在哪些源码结构上看待handler的应用?

    中规中矩的回答就是通过handler处理子线程与主线程切换。实际上这里面考察的面非常广,考察基本功也是深的。

    1. 从设计模式角度看这个问题,完完全全就是多生产者单消费者模式;这个模式典型应用在哪些场景?android里面的toast的实现就很经典了。
    2. 如果这个handler再搭配上子线程,我们会发现internservice就是一个典型的应用场景,先处理完一个intern,然后再处理下一个,单线程,有序地进行。
    3. idlehandler也有用于启动加速的,不过系统里面更典型的应用是activity的destroy生命周期调度,在下一个activity启动之后再通过idlehandler进行上一个destroy的调度,搭配上10s钟超时强制执行destroy,这也是为什么activity回收必然是10s内的原因。
    4. binder通信里面也不缺乏handler的应用。
    5. 还有更多,大家自行发现。

    3、其他的一些细节&具体应用场景问题

    • 同步消息/异步消息是怎么回事?消息栅栏是一个什么情况?
    • messagequeue怎么排序的?handler怎么高效实现优先处理异步消息?
    • 如何实现在manifest文件无声明activity启动?占坑方式的插件化如何实现?
    • 如何解决view的bad token崩溃?
    • 如何统计主线程调用耗时?
    • 如何监控binder进程调用耗时?
    • epoll为什么采用红黑树结构?
    • ......

    结论

    我个人觉得并不是一定要通读源码才是完美无缺的,而是明白其原理,知晓其奥妙,掌握应用场景,知行合一,从更多角度去了解其是如何应用的,然后自己再有足够的技术判断力来觉得其是否适合,那么这个知识就是属于你的。

    相关文章

      网友评论

          本文标题:Handler机制面试中的坑

          本文链接:https://www.haomeiwen.com/subject/dveeedtx.html