当前位置:网站首页>LeakCanary如何监听Service、Root View销毁时机?
LeakCanary如何监听Service、Root View销毁时机?
2022-08-01 10:15:00 【大鱼Ss】
本篇文章讲解LeakCanary如何监听Service、Root View销毁时机并添加到泄漏监听中的。

监听Service销毁时机
我先说下大体的思路:
- 首先我们反射
ActivityThread中属性名为mH的Handler对象,这个对象大家都是很熟悉,AMS通过应用侧的ApplicationThread发送调度通知,然后ApplicationThread将调度操作封装成Meesage并通过这个名为mH的Handler发送出去,然后最终Message被执行,里面调度Acitivity、Service生命周期的操作也自然会被执行了;

- 然后我们反射给mH的属性
mCallback赋值,这个属性对象在处理Message的时机是优先于Handler的handleMessage()方法的,所以我们在这个mCallback是可以监听到Service生命周期调度的。


通过以上方式我们监听到Message的what==116时,就代表Service将执行onDestroy()生命周期,并将这个Service弱引用保存起来;
- 问题来了,Service的
onDestory()什么时候执行完毕呢,这个时候我们就要从源码中寻找解决答案了。看下ActivityThread源码,最终会在handleStopService()执行Service的onDestroy()方法:

可以看到,Service的onDestroy()执行完毕后,就会调用IActivityManager这个Binder对象的方法serviceDoneExecuting()通知AMS。看到这里,是不是明白了怎么做。
- 我们可以通过反射动态代理掉应用侧的
IActivityManager这个Binder对象,并重写其serviceDoneExecuting()方法完成Service的onDestroy()执行完毕的监听。LeakCanary中的源码就是这么做的:
首先动态代理创建一个实现了
IActivityManager接口的对象,这个代理对象里面就会判断当前的方法为serviceDoneExecuting时,代表当前的Service的onDestory()生命周期已经执行完毕,可以对当前Service添加内存泄漏监听了

然后将当前ActivityManager对象中原有的IActivityManager这个
binder对象替换成我们上面动态代理创建的IActivityManager对象

- 通过以上方式,我们就实现了Service销毁时机的监听,并添加到内存泄漏的监听中。
监听Root View销毁时机
这里监听Root View之一就是监听Dialog的根View是否发生了内存泄漏。看下LeakCanary核心代码是如何处理的:

很熟悉的代码,就是拿到Dialog的根View,然后调用addOnAttchStateChangeListener添加监听,并在监听实现类中重写onViewDetachedFromWindow()方法,当根View不可见的时候,就会触发该方法的执行。
至于LeakCanary是怎么统一拿到业务层的Dialog的根View布局的,笔者看了源码也不是很清楚怎么实现的,实现的代码如下,底层的原理就不懂了:

不过上面这种监听Dialog的根View内存泄漏的方式有一个非常严重的问题:
大家在写代码的过程中可能会有一个习惯,在某个类中创建一个全局的成员变量持有这个Dialog,这样方便在全局调用其
show()或者dismiss()方法。
这样就会导致
Dialog执行dismiss()方法关闭弹窗时,Dialog被类的全局变量持有,而Dialog又会持有其根View,所以根View不会被回收销毁的。而这个时候根View已经从屏幕上消息不可见,就会回调onViewDetachedFromWindow()方法,将根View添加到LeakCanary的销毁监听中,这样就会百分之百导致内存泄漏。
上面这种情况下,本身业务类中的某个全局成员属性持有Dialog的行为是很正常的,所以就只能LeakCanary去兼容处理了:

可以看到LeakCanary能不能检测Dialog的Root View(根View)是有开关可以控制的,如果我们项目中存在业务类全局持有Dialog的行为,就可以在xml中将leak_canary_watcher_watch_dismissed_dialogs设置为false即可,这样LeakCanary就不会检测Dialog的Root View销毁了。
作者:长安皈故里
链接:https://juejin.cn/post/7114683266170372104
更多Android相关知识、学习笔记、视频资料详情可扫描下方二维码领取!!

边栏推荐
- MFC实现交通图导航系统
- Small application project works WeChat gourmet recipes applet graduation design of finished product (1) the development profile
- gc的意义和触发条件
- July 31, 2022 -- Take your first steps with C# -- Use arrays and foreach statements in C# to store and iterate through sequences of data
- Android 安全与防护策略
- Google Earth Engine APP——15行代码搞定一个inspector高程监测APP
- 使用ESP32驱动QMA7981读取三轴加速度(带例程)
- Quantify daily work metrics
- .NET性能优化-使用SourceGenerator-Logger记录日志
- STM32入门开发 介绍IIC总线、读写AT24C02(EEPROM)(采用模拟时序)
猜你喜欢

如何从完美的智能合约中窃取 1 亿美元

Batch大小不一定是2的n次幂!ML资深学者最新结论

.NET性能优化-使用SourceGenerator-Logger记录日志

MacOS下postgresql(pgsql)数据库密码为什么不需要填写或可以乱填写

2022年中盘点 | 产品打底,科技背书,广汽集团阔步向前

Change Servlet project to SSM project

Drawing arrows of WPF screenshot control (5) "Imitation WeChat"

Mysql索引相关的知识复盘一

gc的意义和触发条件

小程序毕设作品之微信美食菜谱小程序毕业设计成品(1)开发概要
随机推荐
关于#SQL#的问题,如何解决?
InputStream转成String
MTK6225-紧急电话
IntellJ IDEA如何显示换行符(line endings)
ASP.NET Core 6 Framework Revealing Instance Demonstration [30]: Develop REST API with Routing
Mini Program Graduation Works WeChat Food Recipes Mini Program Graduation Design Finished Products (3) Background Functions
WPF 截图控件之绘制箭头(五)「仿微信」
MacOS下postgresql(pgsql)数据库密码为什么不需要填写或可以乱填写
notes....
可视化——Superset安装与部署
C语言小游戏——扫雷
Drawing arrows of WPF screenshot control (5) "Imitation WeChat"
EasyRecovery热门免费数据检测修复软件
【cartographer ros】十: 延时和误差分析
Qt 支持HEIC/HEIF格式图片
Guangyu Mingdao was selected into the list of pilot demonstration projects for the development of digital economy industry in Chongqing in 2022
rpm and yum
mysql login in cmd and basic operations of database and table
C语言程序设计50例(三)(经典收藏)
自定义类型——枚举、联合