当前位置: 首页 > news >正文

android LeakCanary 2.7 启动流程 详解

LeakCanary 2.7 启动监听流程(基于appDefaultWatchers源码)

以你给出的appDefaultWatchers()源码为切入点,LeakCanary 2.7 的启动流程可以拆解为"自动初始化 → 装配 Watchers → 生命周期 Hook → 引用追踪 → Heap Dump"五个阶段。下面按源码层级逐步展开。


一、架构设计:从appDefaultWatchers看 2.7 的接口化改造

你贴出的源码是 LeakCanary 2.6+ 引入的核心 API,它体现了 2.x 的模块化设计哲学

kotlin

fun appDefaultWatchers( application: Application, reachabilityWatcher: ReachabilityWatcher = objectWatcher ): List<InstallableWatcher> { return listOf( ActivityWatcher(application, reachabilityWatcher), FragmentAndViewModelWatcher(application, reachabilityWatcher), RootViewWatcher(reachabilityWatcher), ServiceWatcher(reachabilityWatcher) ) }

关键设计点:

设计说明
ReachabilityWatcher接口2.6+ 引入的 SAM 接口,由ObjectWatcher实现。四个 Watcher 不直接依赖ObjectWatcher,而是依赖接口,解耦了"监听触发"与"引用追踪"
InstallableWatcher接口每个 Watcher 实现install()方法,负责注册系统回调或反射 Hook。manualInstall()只需遍历调用it.install()即可统一注册
共享objectWatcher四个 Watcher 共享同一个AppWatcher.objectWatcher实例,确保所有被追踪对象都在同一个watchedObjectsMap 中管理

二、自动初始化链路:ContentProvider 触发

leakcanary-android:2.7依赖的AndroidManifest.xml中注册了自动初始化入口:

xml

<provider android:name="leakcanary.internal.AppWatcherInstaller$MainProcess" android:authorities="${applicationId}.leakcanary-installer" android:enabled="@bool/leak_canary_watcher_auto_install" android:exported="false" />

对应的AppWatcherInstaller源码:

kotlin

internal sealed class AppWatcherInstaller : ContentProvider() { internal class MainProcess : AppWatcherInstaller() override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppWatcher.manualInstall(application) // 默认调用,watchersToInstall = appDefaultWatchers(application) return true } }

执行时序Application.attachBaseContext()ContentProvider.onCreate()Application.onCreate()。LeakCanary 在Application.onCreate()之前就已经完成所有 Watcher 的注册。


三、manualInstall内部执行流程

kotlin

@JvmOverloads fun manualInstall( application: Application, retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) ) { checkMainThread() // ① 必须在主线程 if (isInstalled) { // ② 防重入保护 throw IllegalStateException("AppWatcher already installed") } // ③ 加载 LeakCanary 分析引擎(如果 classpath 中存在) LeakCanaryDelegate.loadLeakCanary(application) // ④ 遍历安装所有 Watcher watchersToInstall.forEach { it.install() } }

关键步骤解析:

  1. LeakCanaryDelegate.loadLeakCanary(application):通过反射查找InternalLeakCanary类并实例化,将AppWatcher.objectWatcher注册为OnObjectRetainedListener。这样当ObjectWatcher检测到 retained 对象时,能触发后续的 heap dump。

  2. watchersToInstall.forEach { it.install() }:调用你列出的四个 Watcher 的install()方法,分别向系统注册生命周期监听。


四、四大 Watcher 的注册与实现细节

1.ActivityWatcher

kotlin

internal class ActivityWatcher( private val application: Application, private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks { override fun onActivityDestroyed(activity: Activity) { reachabilityWatcher.expectWeaklyReachable( activity, "Activity received onDestroy() callback" ) } // 其他生命周期方法为空实现 } override fun install() { application.registerActivityLifecycleCallbacks(lifecycleCallbacks) } }

机制:通过Application.registerActivityLifecycleCallbacks()监听所有 Activity 的onDestroy(),销毁时立即将 Activity 实例交给ObjectWatcher追踪。

2.FragmentAndViewModelWatcher

kotlin

internal class FragmentAndViewModelWatcher( private val application: Application, private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { override fun install() { application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { // 注册 Fragment 生命周期回调 (activity as? FragmentActivity)?.supportFragmentManager ?.registerFragmentLifecycleCallbacks(fragmentCallbacks, true) } }) } private val fragmentCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentDestroyed(fm: FragmentManager, fragment: Fragment) { reachabilityWatcher.expectWeaklyReachable(fragment, "Fragment destroyed") // 同时追踪 Fragment 的 View fragment.view?.let { view -> reachabilityWatcher.expectWeaklyReachable(view, "Fragment view destroyed") } } override fun onFragmentViewDestroyed(fm: FragmentManager, fragment: Fragment) { // ViewModel 监听通过 ViewModelStore 实现 watchViewModel(fragment) } } }

机制

  • 利用FragmentManager.FragmentLifecycleCallbacks监听Fragment.onDestroy()

  • Fragment 的 View 和 ViewModel 分别独立追踪

  • ViewModel 通过ViewModelStoreonCleared()回调触发追踪

3.RootViewWatcher(2.7 改进版)

kotlin

internal class RootViewWatcher( private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { override fun install() { // 2.7 使用 Curtains 库替代直接注册 OnAttachStateChangeListener Curtains.onRootViewsChangedListeners += OnRootViewRemovedListener { rootView -> if (rootView.windowType == WindowType.PHONE_WINDOW) { // 2.7 默认忽略 PopupWindow 和 Dialog 的 root view // 可通过资源 bool leak_canary_watcher_watch_dismissed_dialogs 开启 if (rootView.watchDismissedDialogs || rootView !is DialogRootView) { reachabilityWatcher.expectWeaklyReachable( rootView, "View detached from window" ) } } } } }

2.7 关键改进:引入Curtains库来统一监听 Window/RootView 的添加和移除,解决了 2.6 中误报 PopupWindow/Dialog 的问题。

默认行为:

  • ✅ 继续检测 Toast 的 root view 泄漏

  • ❌ 忽略PopupWindow创建的 root view

  • ❌ 忽略Dialog创建的 root view(可通过leak_canary_watcher_watch_dismissed_dialogs资源布尔值开启)

4.ServiceWatcher

kotlin

internal class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher { override fun install() { // Android SDK 没有 Service 生命周期公开 API,通过反射 Hook IActivityManager val activityThreadClass = Class.forName("android.app.ActivityThread") val activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null) // Hook AMS 的 serviceDoneExecuting 方法 val iActivityManager = ActivityManager.getService() val proxy = Proxy.newProxyInstance( iActivityManager.javaClass.classLoader, arrayOf(Class.forName("android.app.IActivityManager")) ) { _, method, args -> if (method.name == "serviceDoneExecuting") { val token = args[0] as IBinder // 通过 token 找到对应的 Service 实例 val service = servicesByToken[token] service?.let { reachabilityWatcher.expectWeaklyReachable( it, "Service received Service#onDestroy() callback" ) } } method.invoke(iActivityManager, *args) } // 替换 ActivityThread 中的 mInstrumentation 或直接使用反射注入 proxy } }

机制:由于 Android SDK 未暴露 Service 销毁的公开监听 API,ServiceWatcher通过反射 + 动态代理 HookIActivityManager来拦截serviceDoneExecuting()调用,从而感知 Service 销毁。


五、ObjectWatcher引用追踪核心

四个 Watcher 最终都调用reachabilityWatcher.expectWeaklyReachable(watchedObject, description),而默认的reachabilityWatcher就是AppWatcher.objectWatcher

kotlin

val objectWatcher = ObjectWatcher( clock = { SystemClock.uptimeMillis() }, checkRetainedExecutor = { // 默认延迟 5s 后执行检查 mainHandler.postDelayed(it, retainedDelayMillis) }, isEnabled = { true } )

ObjectWatcher内部使用ReferenceQueueRetainedObjectTracker

kotlin

class ObjectWatcher : ReachabilityWatcher { private val watchedObjects = mutableMapOf<String, KeyedWeakReference>() private val queue = ReferenceQueue<Any>() private val onObjectRetainedListeners = mutableListOf<OnObjectRetainedListener>() override fun expectWeaklyReachable(watchedObject: Any, description: String) { val key = UUID.randomUUID().toString() // 1. 创建带 key 的弱引用,绑定到 ReferenceQueue val weakRef = KeyedWeakReference(watchedObject, key, description, queue) watchedObjects[key] = weakRef // 2. 延迟 5s 后检查 checkRetainedExecutor.execute { checkRetainedObjects(key) } } private fun checkRetainedObjects(key: String) { removeWeaklyReachableObjects() // 清除已入队的引用 val retainedRef = watchedObjects[key] if (retainedRef != null) { // 3. 手动触发 GC gcTrigger.runGc() removeWeaklyReachableObjects() if (watchedObjects.containsKey(key)) { // 4. GC 后仍未回收 → 通知 LeakCanary onObjectRetainedListeners.forEach { it.onObjectRetained() } } } } }

检测逻辑

  1. 目标对象销毁时,创建KeyedWeakReference(带唯一 key)

  2. 延迟 5s 后检查ReferenceQueue

  3. 若引用已入队 → 对象被回收,无泄漏

  4. 若未入队 → 手动触发Runtime.getRuntime().gc()

  5. GC 后仍未入队 → 对象被保留(retained),通知所有OnObjectRetainedListener


六、从 Retained 到 Heap Dump 的衔接

LeakCanaryDelegate.loadLeakCanary()会将InternalLeakCanary注册为OnObjectRetainedListener

kotlin

internal object InternalLeakCanary : OnObjectRetainedListener { override fun onObjectRetained() { scheduleRetainedObjectCheck() } private fun scheduleRetainedObjectCheck() { backgroundHandler.postDelayed({ val retainedCount = AppWatcher.objectWatcher.retainedObjectCount when { retainedCount == 0 -> return // 前台且未达到阈值,仅显示通知 appVisible && retainedCount < LeakCanary.config.retainedVisibleThreshold -> showRetainedCountNotification(retainedCount) // 后台或达到阈值,执行 heap dump else -> dumpHeap() } }, 2000) } }

触发条件

  • 应用进入后台 → 立即 dump

  • 前台运行 → retained 对象数达到阈值(默认 5 个)才 dump


七、手动安装与自定义(关闭自动初始化)

如需关闭自动初始化并自定义 Watchers:

xml

<!-- res/values/leak_canary.xml --> <resources> <bool name="leak_canary_watcher_auto_install">false</bool> </resources>

然后在Application.onCreate()中手动安装:

kotlin

class DebugApplication : Application() { override fun onCreate() { super.onCreate() // 示例 1:排除 ServiceWatcher(避免反射 Hook 带来的 ANR 风险) val watchersToInstall = AppWatcher.appDefaultWatchers(this) .filter { it !is ServiceWatcher } AppWatcher.manualInstall( application = this, watchersToInstall = watchersToInstall ) // 示例 2:自定义 ReachabilityWatcher 过滤特定类 val delegate = ReachabilityWatcher { watchedObject, description -> if (watchedObject !is BadSdkLeakingFragment) { AppWatcher.objectWatcher.expectWeaklyReachable(watchedObject, description) } } val customWatchers = AppWatcher.appDefaultWatchers(this, delegate) AppWatcher.manualInstall(application = this, watchersToInstall = customWatchers) } }

八、完整启动时序图

应用进程启动 │ ▼ Application.attachBaseContext() │ ▼ ContentProvider.onCreate() ──► AppWatcherInstaller$MainProcess │ │ │ ▼ │ AppWatcher.manualInstall(application) │ │ │ ┌───────────────┼───────────────┐ │ ▼ ▼ ▼ │ LeakCanaryDelegate.loadLeakCanary() appDefaultWatchers(application) │ │ │ │ ▼ ▼ │ InternalLeakCanary.registerListener() [ActivityWatcher, FragmentAndViewModelWatcher, │ RootViewWatcher, ServiceWatcher] │ │ │ ▼ │ watchers.forEach { it.install() } │ │ │ ┌───────────────────────────────┼───────────────┐ │ ▼ ▼ ▼ ▼ │ registerActivityLifecycle registerFragmentLifecycle Curtains.onRootViewsChanged Hook IActivityManager │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ Application.onCreate() ◄──────────────────────────────────────────────────────────────────── │ ▼ 应用正常运行 │ ▼ Activity/Fragment/View/Service 销毁 │ ▼ reachabilityWatcher.expectWeaklyReachable(obj, desc) │ ▼ ObjectWatcher ──► KeyedWeakReference + ReferenceQueue │ ▼ 延迟 5s ──► 检查 ReferenceQueue │ ├── 已清除 ──► 无泄漏 │ └── 未清除 ──► 触发 GC ──► 再次检查 │ ├── 已清除 ──► 无泄漏 │ └── 仍保留 ──► InternalLeakCanary.onObjectRetained() │ ▼ retainedCount >= threshold? │ ├── 否(前台)──► 通知栏提示 retained 数量 │ └── 是 ──► dumpHeap() ──► Shark 分析 ──► 泄漏报告

总结

LeakCanary 2.7 的启动流程以appDefaultWatchers()为装配中心,通过ContentProvider实现零代码侵入自动初始化,通过ReachabilityWatcher接口实现 Watcher 与追踪引擎的解耦,通过InstallableWatcher.install()统一注册系统生命周期回调。四个默认 Watcher 覆盖了 Android 四大组件/核心对象中的 Activity、Fragment、ViewModel、View 和 Service,构成了完整的内存泄漏自动检测体系。

http://www.jsqmd.com/news/1402000/

相关文章:

  • 象形识字偏旁记忆法 教娃认字不用死记硬背了
  • 《牛来》跑通电影审核全流程,OPC一人公司的电影时代正在到来
  • 参数从哪来、何时来 —— 提取时机与平台化 Slot 管理
  • 2026年优选全国铸造工厂:铝合金重力铸造与低压铸造的硬实力解析 - 装修教育财税推荐2026
  • 从零搭建Arduino智能小车:硬件选型、电路连接与避障编程全攻略
  • [2026dasctf]Easy_Bypass
  • 2026 年至今,聊城热门的奥尔良琵琶腿品牌哪个好,花10块钱买这玩意儿,比外卖还香? - 企业信息推荐-2
  • Vibe Coding 实战指南:构建 AI 辅助开发工作流,提升编程效率
  • 2026年阻燃硅胶制品源头供应商实力观察:耐高温防火硅胶条、阻燃硅胶管、阻燃硅胶垫片生产工厂优选参考 - 卓企推荐
  • Python编程练习平台全攻略:从新手到高手的9个实战网站
  • 2026年电动开窗器行业实力厂家推荐:广受信赖的制造企业全景分析 - mypinpai
  • react navite图片加载优化、大图卡顿、缓存策略
  • Git安装配置全指南:从入门到实战
  • 模块化与AI增强的英语学习系统设计与实践
  • 7天挑战项目:高效学习与习惯养成实践指南
  • 极空间NAS部署Ubuntu桌面:打造高性能虚拟化生产力环境
  • STUN服务器搭建与Spring Bean序列化控制实战
  • PyCharm从零到一:安装配置、核心功能与高效开发指南
  • 2026四大一键生成论文工具深度横评|根据论文阶段选工具,事半功倍
  • OpenSSH 10.5安全升级指南:修复关键漏洞与应对新发布策略
  • [开发工具] MCU只写寄存器为什么代码里还能读?新手都容易踩的坑,终于讲清楚了
  • HTML5语义化标签与文档结构详解:从基础到最佳实践
  • 美团钱袋宝:拿下了长期牌照,却没守住合规底线
  • Cursor集成Grok 4.6:AI编程助手从代码补全到项目协作者的进化
  • 2507双相钢现货分销商与S32750优质经销商观察 - 2027品牌AI展
  • 2026大板茶桌实力测评,所见即所得不踩雷 - mypinpai
  • 360CDN SDK游戏盾:DDoS防护核心技术解析与实战
  • 2026 年 8 月新发布:石嘴山可靠的承插式涂塑钢管公司哪家好,埋地管线总漏?这玩意儿竟能把渗漏率降到近乎为零!-浩伦管道 - 行业推荐官-2
  • CSS自适应布局实战:Flexbox与Grid解决方案
  • 2026年北京空调维修选简单到家,这些优势你必须知道 - 简单到家