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,怎么知道集成成功?很简单:

  1. 用Debug变体编译运行App
  2. 在Logcat里过滤 LeakCanary,你应该能看到类似:LeakCanary: LeakCanary is running 的日志
  3. 手动制造一个内存泄漏(比如在Activity里持有一个静态Context),然后退出那个Activity,等几秒,看看是否会弹出通知

如果Logcat没看到日志,多半是依赖没生效,检查一下Build Variants当前是不是Debug。如果通知没弹,检查一下系统通知权限是否打开。

注意:第一次运行建议在真机上测试。模拟器可能因为系统限制或内存不足导致dump失败。我就在模拟器上翻过车——等了5分钟都没反应,以为是没集成成功,结果换到真机秒出结果。

2.7 本章核心流程

下面这张图总结了从依赖添加到初始化验证的完整流程,你对照着走一遍,基本不会出岔子。

LeakCanary 集成核心流程 1. 加Gradle依赖 debugImplementation 2. Application初始化 LeakCanary.config 3. 多模块依赖检查 仅在app模块加 4. 验证日志输出 Logcat过滤LeakCanary 5. 触发测试泄漏 观察是否弹出通知 ⚠️ 最容易漏的坑 debugImplementation写成implementation Release包被带上LeakCanary 导致用户反馈闪退/耗电 ✅ 正确做法 Gradle依赖锁定版本 Application显式配置 只在Debug变体运行

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泄漏定位才能顺利进行。