当前位置:网站首页>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相关知识、学习笔记、视频资料详情可扫描下方二维码领取!!

边栏推荐
- 开天aPaaS之移动手机号码空号检测【开天aPaaS大作战】
- SAP ABAP OData 服务如何支持 $orderby (排序)操作试读版
- Meeting OA (Upcoming Meetings & All Meetings)
- 基于CAP组件实现补偿事务与消息幂等性
- 2022年7月31日--使用C#迈出第一步--使用C#中的数组和foreach语句来存储和循环访问数据序列
- [Software Architecture Mode] The difference between MVVM mode and MVC mode
- Shell: Conditional test action
- CTO strongly banning the use of the Calendar, that in what?
- notes....
- shell脚本------条件测试 if语句和case分支语句
猜你喜欢

Visualization - Superset installation and deployment

How to implement deep copy in js?

Comprehensive experiment BGP

Golang内存分析工具gctrace和pprof实战

7/31 训练日志

Small application project works WeChat gourmet recipes applet graduation design of finished product (1) the development profile

Message queue interview latest finishing (2022)

Mysql index related knowledge review one

ClickHouse多种安装方式

2022年中盘点 | 产品打底,科技背书,广汽集团阔步向前
随机推荐
VS“无法查找或打开PDB文件”是怎么回事?如何解决
RK3399平台开发系列讲解(内核入门篇)1.52、printk函数分析 - 其函数调用时候会关闭中断
notes....
How I secured 70,000 ETH and won a 6 million bug bounty
The meaning and trigger conditions of gc
Basic configuration commands of cisco switches (what is the save command of Huawei switches)
记一次 .NET 某智慧物流WCS系统CPU爆高分析
Android Security and Protection Policy
shell脚本------条件测试 if语句和case分支语句
小程序毕设作品之微信美食菜谱小程序毕业设计成品(3)后台功能
C language game - minesweeper
Browser shortcut keys
What's up with VS "Cannot find or open PDB file"?How to solve
小程序毕设作品之微信美食菜谱小程序毕业设计成品(4)开题报告
如何解决 chrome 浏览器标签过多无法查看到标题的情况
什么是步进电机?40张图带你了解!
微信公众号授权登录后报redirect_uri参数错误的问题
ClickHouse入门介绍与其特性
【软件架构模式】MVVM模式和MVC模式区别
解决new Thread().Start导致高并发CPU 100%的问题