2、项目集成:加依赖、初始化,Debug变体别漏了
好,咱们正式开始动手。集成LeakCanary其实就两步:加依赖 + 初始化。听起来简单吧?但就这两步,我见过不止一个同事翻车——原因嘛,基本都出在debugImplementation上。你想想看,线上包带上LeakCanary,用户手机里跑个内存检测工具,既耗电又容易误报,谁受得了?所以官方特意把它设计成只在Debug变体生效。
下面我带你一步步走,顺便把几个容易踩的坑提前排掉。
2.1 添加Gradle依赖
打开模块的build.gradle(注意是app或者你目标模块的,别跑到根目录去了)。在dependencies块里加上这一行:
dependencies {
// LeakCanary 2.x 版本
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
}
嗯,就一行?对,就一行。为什么没有releaseImplementation或compileOnly?因为LeakCanary 2.x以后,它不需要额外的无操作空壳包了。debugImplementation意味着只在debug构建变体里生效,Release包完全不受影响。
关键点:
- 务必使用
debugImplementation,不要写成implementation或api。 - 版本号建议锁定,避免自动升级带来兼容问题。我一般用
2.14,比较稳定。
2.2 版本选择的坑
你可能在网上看到一些教程还在用1.x版本的写法——比如debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'再加一个releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'。千万别学!那是老黄历了。2.x版本把两件事合并了:检测 + 展示全在leakcanary-android里,Release变体下的debugImplementation直接被Gradle忽略,干干净净。
我记得有一次帮同事排查,他用的是implementation,结果Release包也带上了LeakCanary,一打开App就弹通知,用户差点投诉……后来改成debugImplementation就没事了。
2.3 在Application中初始化
依赖加完,接下来是在Application类里初始化。LeakCanary 2.x的初始化非常简单:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 如果你只想用默认配置,这一行就够了
LeakCanary.config = LeakCanary.config
.copy(
maxStoredHeapDumps = 5,
referenceMatchers = AndroidReferenceMatchers.appDefaults
.plus(MyCustomMatcher())
)
}
}
等等,上面代码看起来有点复杂?其实默认配置下,你连这行都不需要写。LeakCanary 2.x通过ContentProvider自动初始化,只要你加了依赖,它就会在App启动时自动注册。但我们做BLE内存泄漏定位,往往需要定制一些东西,比如:
- 限制保存的dump数量,防止撑爆存储
- 添加自定义的引用匹配器(比如过滤掉某些BLE回调的临时对象)
- 设置通知渠道或者静默模式
所以我会保留onCreate里的配置,哪怕只改一个参数。为啥?因为后面你要加自定义匹配器时,不用再翻代码找入口。
我的习惯:先让LeakCanary跑默认配置验证一次,确认它能正常抓取泄漏。然后再加定制项。这样出了问题容易定位是配置问题还是代码问题。
2.4 千万别漏掉的初始化检查清单
每次集成都容易漏东西,我列个表,你对照着打勾:
| 检查项 | 说明 |
|---|---|
Gradle依赖用 debugImplementation |
不能用 implementation 或 api |
| 版本号是否锁定 | 比如 2.14,不要用 2.+ |
| 是否有其他LeakCanary冲突 | 如果项目里引用了某些第三方库内部也带了LeakCanary,注意排除 |
Application中是否调用了 LeakCanary.config |
即使默认配置也要显式写一行,方便后续修改 |
| 多模块项目依赖链 | 确保所有模块都能访问到 leakcanary-android |
| ProGuard/R8是否混淆了LeakCanary | Debug变体一般不开混淆,但注意了,有些项目Debug也开了混淆规则 |
2.5 多模块项目怎么配?
如果你的项目是多模块(比如app、core、feature_ble),LeakCanary只需要在主app模块加依赖就行。因为它会自动监测所有进程中Activity、Fragment、ViewModel等对象的泄漏。不过注意:如果某个模块有自己的Application类(比如Library类型的模块),LeakCanary不会自动监测那个进程。但是一般我们只会在App主进程里跑检测,所以问题不大。
我曾经遇到过一种情况:BLE相关的业务写在:lib_ble模块里,这个模块内部持有了BluetoothGatt对象,而主进程销毁了,但BluetoothGatt的回调线程还在跑,导致泄漏。LeakCanary能抓到吗?能,因为回调线程属于主进程。所以放心。
2.6 集成后的验证方式
加完依赖、配好Application,怎么知道集成成功?很简单:
- 用Debug变体编译运行App
- 在Logcat里过滤
LeakCanary,你应该能看到类似:LeakCanary: LeakCanary is running的日志 - 手动制造一个内存泄漏(比如在Activity里持有一个静态Context),然后退出那个Activity,等几秒,看看是否会弹出通知
如果Logcat没看到日志,多半是依赖没生效,检查一下Build Variants当前是不是Debug。如果通知没弹,检查一下系统通知权限是否打开。
注意:第一次运行建议在真机上测试。模拟器可能因为系统限制或内存不足导致dump失败。我就在模拟器上翻过车——等了5分钟都没反应,以为是没集成成功,结果换到真机秒出结果。
2.7 本章核心流程
下面这张图总结了从依赖添加到初始化验证的完整流程,你对照着走一遍,基本不会出岔子。
2.8 避坑总结
集成这一步,说难真不难,但陷阱就藏在细节里。我再强调几个重点:
- debugImplementation —— 我见过有同事用
implementation,结果Release包也带上了LeakCanary,测试时没发现,上线后被用户吐槽通知太多。只好紧急发版去掉。那叫一个狼狈。 - 多模块注意事项 —— 如果你在
feature_ble模块里也加了LeakCanary依赖,而且用了implementation,那么当主模块没有依赖时,编译不报错,但运行时会报NoClassDefFoundError。所以统一只在app模块加就行。 - ProGuard/R8 —— Debug变体默认不开混淆,但如果你手动配置了,记得保留下LeakCanary的类:
-keep class leakcanary.** { *; } - Android 12+通知 —— LeakCanary检测到泄漏后会发通知,Android 12以上需要动态申请
POST_NOTIFICATIONS权限,否则通知不弹。不过你不用改代码,App首次启动时LeakCanary会提示你授权。
记住一句话:依赖配不对,后面全白费。集成这一步花10分钟弄好了,后续的BLE泄漏定位才能顺利进行。