LeakCanary2.3 核心原理浅析

本文详细介绍了LeakCanary 2.3的核心原理,包括自动初始化、Activity和Fragment的监测、内存泄漏检测流程、heap dump及解析、结果通知展示等。通过对源码的深入分析,揭示了LeakCanary如何帮助开发者检测和防止Android应用的内存泄漏问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

LeakCanary是Android开发中常用的用来检测内存泄漏的框架,它能够帮助开发者快速发现是否发生内存泄漏,并可视化的给予提示。

这里基于LeakCanary 2.3版本进行分析。

只需要在工程中进行简单的集成配置,就能自动对Activity、Fragment、fragment View进行自动检测。也可以通过ObjectWatcher#watch方法对指定对象进行检测。

dependencies {
   
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
}

仅需添加一行依赖,就完成了接入工作,当buildTypes为debug时,运行APP就会自动启动LeakCanary。

核心流程拆解

LeakCanary的核心功能,大致可以分为两块:

  • 检查对象是否被释放,若未被及时回收,则可能发生内存泄漏
  • dump heap并解析hprof文件,查找检测的对象生成引用链信息

检测泄漏功能的主要流程如下:

  1. SDK自初始化
    在1.X是通过手动调用方法初始化,2.X实现自动初始化。

  2. 自动对Android特定对象进行检测
    例如自动监测Activity、Fragment、fragment View生命周期销毁。

  3. 检测判断对象是否发生内存泄漏
    检查一个对象应该被回收,可能产生内存泄漏而没有被回收。

  4. dump heap至hprof文件并解析文件,生成泄漏引用链
    依赖另一个专门分析hprof的库来解析文件和生成分析结果。在1.X是依赖haha库,2.X改成依赖shark库。

  5. 在通知栏和Activity界面显示泄漏信息
    把泄漏分析结果发送到通知栏和Activity界面中显示可能导致泄漏的对象引用链。

源码探究

SDK自动初始化

前面看到LeakCanary只需要添加一行依赖,不需要开发者手动调用它的方法,那么它是如何自己把自己启动起来呢?可以猜测通过注册静态广播监听系统广播事件或者注册ContentProvider。

查找LeakCanary开源库中的AndroidManifest.xml,可以发现没有符合的静态注册广播,而存在ContentProvider:

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

位于AndroidManifest.xml

在AppWatcherInstaller中会进行LeakCanary的初始化。

关于ContentProvider初始化

清单中注册的ContentProvider在应用启动的时候就会进行初始化:

[ActivityThread#handleBindApplication]

private void handleBindApplication(AppBindData data) {
   
    // ···
    Application app;
    // ···
    // 反射实例化Application,并会调用其attachBaseContext方法
    app = data.info.makeApplication(data.restrictedBackupMode, null);
    // ···
    // 初始化注册的ContentProvider
    installContentProviders(app, data.providers);
    // ···
    // 触发Application的onCreate回调
    mInstrumentation.callApplicationOnCreate(app);
    // ···
}

ContentProvider启动后就会回调onCreate方法。

AppWatcherInstaller

AppWatcherInstaller继承ContentProvider,在其onCreate回调中,调用InternalAppWatcher#install方法进行初始化。

InternalAppWatcher

InternalAppWatcher负责SDK的初始化。

// 初始化ObjectWatcher,用于检测传入的对象是否未被及时回收
val objectWatcher = ObjectWatcher(
    clock = clock,
    checkRetainedExecutor = checkRetainedExecutor,
    isEnabled = {
    AppWatcher.config.enabled }
)

接着看它的init代码块:

init {
   
  // 通过反射获取InternalLeakCanary实例
  val internalLeakCanary = try {
   
    val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
    leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null)
  } catch (ignored: Throwable) {
   
    NoLeakCanary
  }
  @kotlin.Suppress("UNCHECKED_CAST")
  // onAppWatcherInstalled指向InternalLeakCanary#invoke(Application)方法
  onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
}

接着看install方法:

fun install(application: Application) {
   
  SharkLog.logger = DefaultCanaryLog()
  // 检查是否在主线程,不在主线程则抛异常
  checkMainThread()
  if (this::application.isInitialized) {
   
    return
  }
  InternalAppWatcher.application = application

  val configProvider = {
    AppWatcher.config }
  // Activity自动监测相关初始化
  ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
  // Fragment自动监测相关初始化
  FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
  // 在init代码块中,onAppWatcherInstalled被赋值为InternalLeakCanary#invoke
  onAppWatcherInstalled(application)
}

该方法中进行了Activity和Fragment自动监测功能的初始化,最后初始化InternalLeakCanary。

接着看InternalLeakCanary的invoke方法:

override fun invoke(application: Application) {
   
  this.application = application

  // 检查是否在DEBUGGABLE启动,若不是则抛异常(除非开启设置)
  checkRunningInDebuggableBuild()

  // 添加OnObjectRetainedListener监听,单发生对象未及时回收时,会回调onObjectRetained
  AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

  // 用于dump heap
  val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)

  // 用于主动触发gc
  val gcTrigger = GcTrigger.Default

  val configProvider = {
    LeakCanary.config }

  // 开辟子线程,创建子线程通信handler
  val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
  handlerThread.start()
  val backgroundHandler = Handler(handlerThread.looper)

  // 用于调度dump heap
  heapDumpTrigger = HeapDumpTrigger(
      application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
      configProvider
  )
  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值