![](https://img.haomeiwen.com/i4488303/647c69a03e051cc6.png)
Handler在Android 一个app内部使用 不会跨进程使用,解决线程之间的问题
面试相关问题:
![](https://img.haomeiwen.com/i4488303/50f3ec7c700c9114.png)
1.一个线程几个handler?
handler是个类 在一个线程中可以有n多个。比如可以为主线程创建多个,但是一个线程只有一个looper
![](https://img.haomeiwen.com/i4488303/32f2cec8995fc0e9.png)
所有与主线程通信的底层逻辑都是使用的handler通信
使用过程中可能会导致内存泄漏
2.线程间通信的具体原理(内存共享)
工作流程
![](https://img.haomeiwen.com/i4488303/37ef660158bd0874.png)
![](https://img.haomeiwen.com/i4488303/7aa1bd094b6f1858.png)
![](https://img.haomeiwen.com/i4488303/d9c1996d76228e3a.png)
![](https://img.haomeiwen.com/i4488303/7cc4c085c761a550.png)
message作为一个内存块,从子线程到主线程,再取出来用
-
内存泄漏
3.1内部类为什么会持有外部类的引用(最简单的一知半解的解释)
延伸知识点:
image.png
通过引用链分析是否存在GCRoots,如果存在可能存在内存泄漏的风险
面试:内部类为什么会持有外部类的引用
这是因为内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class文件,其中内部类的构造函数中会传入外部类的实例,然后就可以通过this$0访问外部类的成员。
其实也挺好理解的吧,因为在内部类中可以调用外部类的方法,变量等等,所以肯定会持有外部类的引用的。
相关链接:https://blog.csdn.net/cpcpcp123/article/details/122000663
3.2handler中 在activity中new了一个handler 则handler持有了外部activity的对象,那谁持有了handler对象呢?
Framwork模块学习handler讲解部分5
这是问题关键源码分析如下:
![](https://img.haomeiwen.com/i4488303/ea21583dacb147af.png)
其中this 就是 handler对象,handler被message持有了,而message被handler发送的时候被加入到了MessageQueue中又被MessageQueue持有了 ,而MessageQueue是handler初始化的时候被looper持有的 如下图源码
![](https://img.haomeiwen.com/i4488303/f7c49ba4e13af507.png)
![](https://img.haomeiwen.com/i4488303/521fe4a82550a723.png)
![](https://img.haomeiwen.com/i4488303/353efb08f42ff80e.png)
MessageQueue又是final类型 一旦创建是不可改的
而looper初始化是Looper.prepareMainLooper 跟踪源码如下:
![](https://img.haomeiwen.com/i4488303/208d622a4c39707c.png)
![](https://img.haomeiwen.com/i4488303/3f271278fe5ceaec.png)
![](https://img.haomeiwen.com/i4488303/29819b05c9a62907.png)
关键问题分析,正常发送的message如果被Activity即使处理了就不会导致内存泄漏;但是如果发送的是延迟20s才收到,activity在没有收到之前就已经finish掉了,这种情况就导致GCROOTS持有链一直都在持有就无法释放;解决办法1如下:
![](https://img.haomeiwen.com/i4488303/f4888d0af6c43316.png)
声明为static的内部类,因为static的内部类不会持有外部当前Activity的引用对象,但是handle对象怎么才调用activity的方法呢?那就再弱或软引用当前的activity进行引用;
还有其它的方法比如activity销毁的时候直接remove掉MessageQueue中的消息这样的话也会阻断这个持有链 这样GCROOTS就不会引用它了 就会被回收了。
![](https://img.haomeiwen.com/i4488303/48aa966b47107ff2.png)
主线程new handler的时候 在handler的构造方法会去调用取到Looper.myLooper对象,而这个Looper对象是在app启动的时候在ActivityThread中已经在当前主线程创建好了,所以可以取到。所以在子线程创建handler的时候 子线程中没有looper对象要提前prepare好
5.![](https://img.haomeiwen.com/i4488303/83bf6052e44a8d90.png)
wait()会等notifyAll来唤起
6.epoll机制 linux内核中的一种可拓展文件IO事件处理机制,一种管理阻塞的机制底层是Linux,只有研究Linux系统的才会研究epoll , linux 2.3之后 出现epoll机制 研究C和C++的也都不去研究源码,java的Ngix就是基于epoll的机制
epoll之前会遍历所有的io流列表 不管是不是与本流相关 如下: 复杂度O(n)
![](https://img.haomeiwen.com/i4488303/8cb45a3231124be0.png)
epoll之后 只遍历相关的io流 提升了性能复杂度O(1) epoll_wait会把相关的流赋值给steam,
![](https://img.haomeiwen.com/i4488303/ce6aa06b9a33d894.png)
epoll创建是在MessageQueue创建的时候Init初始化的,初始化的时候便把事件注册函数注册了
![](https://img.haomeiwen.com/i4488303/5f813a329b285b62.png)
wait的过程中是如何唤醒的呢 往消息队列放消息的时候 此处唤醒的是一个epoll所对应的事件,此过程是在Linux内部看不懂你的另一个字节码处理的
![](https://img.haomeiwen.com/i4488303/63cb916b7c850d5f.png)
7 同步屏障
如下图正常消息队列是挨个同步遍历的,但是如果保证这个消息还没遍历到就刷新呢,就用到了消息屏障
![](https://img.haomeiwen.com/i4488303/0369a4a7e93afe41.png)
屏障消息target为null;
![](https://img.haomeiwen.com/i4488303/b4b51832e3636e46.png)
消息分类三种类型:
上图中白色的为正常的消息;红色的是屏障消息;黄色的是异步部消息
设置消息屏障目的就是级别高的先执行
整个设计流程:
也就是,只要有一个屏障消息为第一个消息 ,那么相当于再这个时间添加了屏障(这里不会主动唤醒线程) ,那么后面只要后面入队的消息为异步消息 都优先执行,没有则一直阻塞,如果这个时候一个普通消息sendMessageDelayed(getPostMessage®, 0)入队 会触发唤醒线程,有异步消息 则取出此异步消息返回 然后继续阻塞线程 , 直到移除屏障消息(这里才会触发唤醒线程)。没有异步消息 则取出这个普通消息 返回。
关于消息屏障的触发时机 详细解析:
https://blog.csdn.net/z936689039/article/details/128989367
![](https://img.haomeiwen.com/i4488303/1c4e686a5c165a5d.png)
![](https://img.haomeiwen.com/i4488303/c7fb7a392dbfa0d5.png)
![](https://img.haomeiwen.com/i4488303/0771ac09f03a68b5.png)
11 Looper死循环为啥不会ANR 解析如下 不是一个概念
![](https://img.haomeiwen.com/i4488303/ca7df8c1ed2acfac.png)
网友评论