LeakCanary源码解析

178 阅读5分钟

LeakCanary 的启动

在引入LeakCanary的时候,只需要在app/build.gradle中加入下面这行配置即可:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

从引入方式可以看到,用的是debugImplementation方式,即只在debug模式的编译和最终的debug apk打包时有效,LeakCanary在分析时会影响一定性能的,影响app的运行速度,因此只在debug模式下使用

我们在使用的过程中发现根本没有在Application中调用init()初始化LeakCanary,那LeakCanary是怎么工作的?

通过查看引入库的源码AndroidManifest.xml文件,原来LeakCanary是使用ContentProvider自动初始化,不需要手动调用install方法。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.squareup.leakcanary.objectwatcher" >

    <uses-sdk android:minSdkVersion="14" />

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

</manifest>

可以看到AppWatcherInstaller继承自ContentProvider。

当我们启动App时,这里指的是冷启动,在ActivityTaskSupervisor这个类中startSpecificActivity方法中会判断启动的App进程是否存在,如果是冷启动的情况:

mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");

会通过ATMS将进程创建出来,Post消息以启动进程,以避免在ATM锁保持的情况下调用AMS的可能死锁。

final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
        mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
        isTop, hostingType, activity.intent.getComponent());
mH.sendMessage(m);

使用socket方式通知zygote去fork新的进程,这个进程的入口就会在android.app.ActivityThread.javamain方法。

thread.attach(false, startSeq);

继续看attach方法

try {
    mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
    throw ex.rethrowFromSystemServer();
}

进到AMS中的attachApplication

thread.bindApplication(processName, appInfo, providerList,
        instr2.mClass,
        profilerInfo, instr2.mArguments,
        instr2.mWatcher,
        instr2.mUiAutomationConnection, testMode,
        mBinderTransactionTrackingEnabled, enableTrackAllocation,
        isRestrictedBackupMode || !normalMode, app.isPersistent(),
        new Configuration(app.getWindowProcessController().getConfiguration()),
        app.getCompat(), getCommonServicesLocked(app.isolated),
        mCoreSettingsObserver.getCoreSettingsLocked(),
        buildSerial, autofillOptions, contentCaptureOptions,
        app.getDisabledCompatChanges(), serializedSystemFontMap);

再进入到ActivityThread的bindApplication,通过Handler在进到handleBindApplication,终于看到注册ContentProvider的方法,可以看到Application创建后的回调方法在注册ContentProvider之后

app = data.info.makeApplication(data.restrictedBackupMode, null);
...
if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
    }
}
...
try {
    mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
    if (!mInstrumentation.onException(app, e)) {
        throw new RuntimeException(
          "Unable to create application " + app.getClass().getName()
          + ": " + e.toString(), e);
    }
}

这样 AppWatcherInstaller 就会被调用。

internal sealed class AppWatcherInstaller : ContentProvider() {

  /**
   * [MainProcess] automatically sets up the LeakCanary code that runs in the main app process.
   */
  internal class MainProcess : AppWatcherInstaller()

  /**
   * When using the `leakcanary-android-process` artifact instead of `leakcanary-android`,
   * [LeakCanaryProcess] automatically sets up the LeakCanary code
   */
  internal class LeakCanaryProcess : AppWatcherInstaller()

  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }

可以看到:LeakCanary是用kotlin写的,在ContentProvider的onCreate()方法内执行了AppWatcher.manualInstall(application),检测内存泄露逻辑就从这个方法开始了,接下来通过这个方法一步一步对源码进行分析。

这里有个application的变量是获取到Application,但是这个ContentProvider不是在Application.onCreate之前就启动了么?那是如何获取到Application的

@JvmOverloads
fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),//延迟5s
  watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) //需要监控的对象
) {
  checkMainThread()
  //检查是否是主线程
  if (isInstalled) {
    throw IllegalStateException(
      "AppWatcher already installed, see exception cause for prior install call", installCause
    )
  }
  check(retainedDelayMillis >= 0) {
    "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
  }
  installCause = RuntimeException("manualInstall() first called here")
  this.retainedDelayMillis = retainedDelayMillis
  if (application.isDebuggableBuild) {
    LogcatSharkLog.install()
  }
  // Requires AppWatcher.objectWatcher to be set
  //采用反射形式进行初始化
  LeakCanaryDelegate.loadLeakCanary(application)

  watchersToInstall.forEach {
  //添加监控对象的回调
      it.install()
  }
}

manualInstall 是一个很重要的方法,主要是启动AppWatcher的使用,并且其参数也是需要细细看的,第二个参数是延时时间5s,也就是延迟 5s 再去进行内存泄漏的检测。第三个参数就是需要监控对象的list。来看看都有哪些对象:

fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
  return listOf(
    //avtivity监听
    ActivityWatcher(application, reachabilityWatcher),
    //fragment监听
    FragmentAndViewModelWatcher(application, reachabilityWatcher),
    //view监听
    RootViewWatcher(reachabilityWatcher),
    //service监听
    ServiceWatcher(reachabilityWatcher)
  )
}
  • activity,通过 Application.ActivityLifecycleCallbacks 来判断 activity 是否已经销毁了;
  • fragment,fragment 的不同版本,会有不同的处理,具体可以参考 AndroidSupportFragmentDestroyWatcher, AndroidOFragmentDestroyWatcher,AndroidXFragmentDestroyWatcher 这三个类。其中还包含了对 viewModel 的监控
  • rootview,通过 OnRootViewAddedListener 来进行监控,当 android.view.WindowManager.addView 调用的时候,会对其 onRootViewAdded 进行回调,从而可以获得 rootview 。 
  • service,这里比较复杂,需要了解相关的源码。主要是利用反射来获取 service 相关的通知。比如获取到 mH 的 mCallback,并把自己的 callback 交给 mH,这样当 mh 收到消息就会回调 callback,然后再去调用拦截的 mCallback,这样就不会改变原有的运行轨迹。
internal object LeakCanaryDelegate {

  @Suppress("UNCHECKED_CAST")
  val loadLeakCanary by lazy {
    try {
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null) as (Application) -> Unit
    } catch (ignored: Throwable) {
      NoLeakCanary
    }
  }

  object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    override fun invoke(application: Application) {
    }

    override fun onObjectRetained() {
    }
  }
}

可以发现这里反射来获取InternalLeakCanary的实例,前面调用的方式LeakCanaryDelegate.loadLeakCanary(application),这会触发 LeakCanaryDelegate 中的 invoke 方法。

那为什么会触发呢,因为 LeakCanaryDelegate 继承了一个函数 

internal object InternalLeakCanary : (Application) -> Unit

接着看InternalLeakCanary.invoke方法即可,该方法也是完成了一些初始化操作

override fun invoke(application: Application) {
  _application = application

  checkRunningInDebuggableBuild()

//添加回调
  AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

  val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))

  val gcTrigger = GcTrigger.Default

  val configProvider = { LeakCanary.config }

//提供一个后台线程的looper
  val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
  handlerThread.start()
  val backgroundHandler = Handler(handlerThread.looper)
  
//初始化heapDump触发器
  heapDumpTrigger = HeapDumpTrigger(
    application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
    configProvider
  )
  //添加可见性回调
  application.registerVisibilityListener { applicationVisible ->
    this.applicationVisible = applicationVisible
    heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
  }
  //对Activity状态的监听
  registerResumedActivityListener(application)
  addDynamicShortcut(application)

  // We post so that the log happens after Application.onCreate()
  mainHandler.post {
    // https://github.com/square/leakcanary/issues/1981
    // We post to a background handler because HeapDumpControl.iCanHasHeap() checks a shared pref
    // which blocks until loaded and that creates a StrictMode violation.
    backgroundHandler.post {
      SharkLog.d {
        when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) {
          is Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text)
          is Nope -> application.getString(
            R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
          )
        }
      }
    }
  }
}

可以发现 invoke 才是 LeakCanary 启动后初始化的核心逻辑。在这里注册了很多回调,启动了后台线程,heapdump 触发器,gc 触发器等。到这里,关于 LeakCanary 的启动逻辑就讲完了。

如何触发检测

在AppWatcher.manualInstall方法中就提到四个监控对象,当四个对象的生命周期发生变化的时候,就会触发相应的检测流程。以ActivityWatcher为例:

class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
}

通过执行Application.registerActivityLifecycleCallbacks()注册Activity生命周期中Destroyed的监听,在onActivityDestroyed()中 添加reachabilityWatcher的回调,reachabilityWatcher就是AppWatcher中的objectWatcher这个变量

val objectWatcher = ObjectWatcher(
  clock = { SystemClock.uptimeMillis() },
  checkRetainedExecutor = {
    check(isInstalled) {
      "AppWatcher not installed"
    }
    mainHandler.postDelayed(it, retainedDelayMillis)
  },
  isEnabled = { true }
)

因此,接下去我们需要去看看  ObjectWatcher 这个类的相关逻辑了。

@Synchronized override fun expectWeaklyReachable(
  watchedObject: Any,
  description: String
) {
  if (!isEnabled()) {//一般为true
    return
  }
  //先将一些已经回收的监控对象删除
  removeWeaklyReachableObjects()
  //获取唯一的标识
  val key = UUID.randomUUID()
    .toString()
  val watchUptimeMillis = clock.uptimeMillis()
  //创建一个观察对象
  val reference =
    KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
  SharkLog.d {
    "Watching " +
      (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
      (if (description.isNotEmpty()) " ($description)" else "") +
      " with key $key"
  }
//加入观察map中
  watchedObjects[key] = reference
  checkRetainedExecutor.execute {
  //可以知道5s后才会执行
    moveToRetained(key)
  }
}

expectWeaklyReachable所做的事情很简单,具体如下:

1.removeWeaklyReachableObjects先把已经回收的监控对象从watchedObjects中删除;

2.通过唯一表示key,当前时间戳来为当前需要监控的对象构造一个KeyedWeakReference,并且所有的监控对象都是共用一个queue;

3.把监控对象添加到watchedObjects中;

这里有个很关键的类KeyedWeakReference,下面来具体看看这个类的实现:

class KeyedWeakReference(
  referent: Any,
  val key: String,
  val description: String,
  val watchUptimeMillis: Long,
  referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
  referent, referenceQueue
)

我们惊喜的发现KeyedWeakReference继承自WeakReference,并且新增了一些额外的参数。

这里通过 activity 为例子介绍了触发检测的逻辑,所有监控对象都是在监听到其被销毁的时候才会触发检测,一旦销毁了就会把监控对象放在 watchedObjects,等待5s后再来看是否已经被回收。

回收操作

等待5s之后再来看看moveToRetained 的具体逻辑:

@Synchronized private fun moveToRetained(key: String) {
  removeWeaklyReachableObjects()
  val retainedRef = watchedObjects[key]
  if (retainedRef != null) {
    retainedRef.retainedUptimeMillis = clock.uptimeMillis()
    onObjectRetainedListeners.forEach { it.onObjectRetained() }
  }
}

可以看到再次调用了removeWeaklyReachableObjects()方法,也就是5s后,再次把已经回收的监控对象从watchedObjects中删除。

这里并不是对watchedObjects进行遍历来判断是否回收的,而是从queue中取出来对应key的对象,如果取不到就表示该对象已经被回收了,内存没有泄露;如果取到了(retainedRef != null)就表示该对象依然存活,内存泄露了。

判断对象依然存活之后可以看到更新存活对象的保留时间,触发InternalLeakCanary中的onObjectRetained()回调对泄露的对象进行检测.

override fun onObjectRetained() = scheduleRetainedObjectCheck()

fun scheduleRetainedObjectCheck() {
  if (this::heapDumpTrigger.isInitialized) {
    heapDumpTrigger.scheduleRetainedObjectCheck()
  }
}

这里调用 HeapDumpTrigger 来对存活的对象进行检测,下面看看具体的检测逻辑:

fun scheduleRetainedObjectCheck(
  delayMillis: Long = 0L
) {
  val checkCurrentlyScheduledAt = checkScheduledAt
  if (checkCurrentlyScheduledAt > 0) {
    return
  }
  checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
  backgroundHandler.postDelayed({
    checkScheduledAt = 0
    checkRetainedObjects()
  }, delayMillis)
}
private fun checkRetainedObjects() {
  val iCanHasHeap = HeapDumpControl.iCanHasHeap()

  val config = configProvider()

  if (iCanHasHeap is Nope) {
    if (iCanHasHeap is NotifyingNope) {
      // Before notifying that we can't dump heap, let's check if we still have retained object.
      var retainedReferenceCount = objectWatcher.retainedObjectCount

      if (retainedReferenceCount > 0) {
        //触发GC
        gcTrigger.runGc()
        //未被回收对象数量
        retainedReferenceCount = objectWatcher.retainedObjectCount
      }

      val nopeReason = iCanHasHeap.reason()
      val wouldDump = !checkRetainedCount(
        retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
      )

      if (wouldDump) {
        val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
        onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
        showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = uppercaseReason
        )
      }
    } else {
      SharkLog.d {
        application.getString(
          R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
        )
      }
    }
    return
  }

  var retainedReferenceCount = objectWatcher.retainedObjectCount

  if (retainedReferenceCount > 0) {
    gcTrigger.runGc()
    retainedReferenceCount = objectWatcher.retainedObjectCount
  }

// 判断剩下的数量小于规定数量直接返回,默认是5个起步
  if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
    onRetainInstanceListener.onEvent(DumpHappenedRecently)
    showRetainedCountNotification(
      objectCount = retainedReferenceCount,
      contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
    )
    scheduleRetainedObjectCheck(
      delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
    )
    return
  }

  dismissRetainedCountNotification()
  val visibility = if (applicationVisible) "visible" else "not visible"
  //进行heap dump
  dumpHeap(
    retainedReferenceCount = retainedReferenceCount,
    retry = true,
    reason = "$retainedReferenceCount retained objects, app is $visibility"
  )
}

代码比较长,整理下相关知识点:

1.如果保留对象(未回收对象)的数量大于0,则进行一次GC,避免额外的转储堆,尽可能的将对象回收;

2.默认情况下,保留对象的数量<5,不会进行转储堆,节省资源

3.如果两次转储堆之间时间少于60s,也会直接返回,避免频繁转储堆

4.调用dunpHeap()进行真正的转储堆操作

5.当然在真正进行dump前,还需要依赖ICanHazHeap来判断是否可以进行转储堆,里面会做一些检查,确保转储堆的条件是满足的

转储堆

对于实在无法回收的,这时候就采用转储堆来将其显出原形

private fun dumpHeap(
  retainedReferenceCount: Int,
  retry: Boolean,
  reason: String
) {
  saveResourceIdNamesToMemory()
  val heapDumpUptimeMillis = SystemClock.uptimeMillis()
  KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
  when (val heapDumpResult = heapDumper.dumpHeap()) {
    //没有dump
    is NoHeapDump -> {
      if (retry) {
        SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
        scheduleRetainedObjectCheck(
          delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
        )
      } else {
        SharkLog.d { "Failed to dump heap, will not automatically retry" }
      }
      //显示dump失败通知
      showRetainedCountNotification(
        objectCount = retainedReferenceCount,
        contentText = application.getString(
          R.string.leak_canary_notification_retained_dump_failed
        )
      )
    }
    //dump成功
    is HeapDump -> {
      lastDisplayedRetainedObjectCount = 0
      lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
      objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
      HeapAnalyzerService.runAnalysis(
        context = application,
        heapDumpFile = heapDumpResult.file,
        heapDumpDurationMillis = heapDumpResult.durationMillis,
        heapDumpReason = reason
      )
    }
  }
}

HeapDumpTrigger 如其名,就是一个 dump 触发器,这里最终是调用 AndroidHeapDumper 来进行 dump 的,最后会得到 dump 的结果。

可以看到上述主要讲结果分为两类,一个是 NoHeapDump,如果需要继续尝试的话,会延迟一段时间后继续重试。另一个结果自然就是成功了。

暂时先不看结果,这里先来看看 AndroidHeapDumper dump 过程,具体代码如下:

override fun dumpHeap(): DumpHeapResult {
  //获取文件名,如果为null,就直接返回
  val heapDumpFile = leakDirectoryProvider.newHeapDumpFile() ?: return NoHeapDump

  val waitingForToast = FutureResult<Toast?>()
  showToast(waitingForToast)

  if (!waitingForToast.wait(5, SECONDS)) {
    SharkLog.d { "Did not dump heap, too much time waiting for Toast." }
    return NoHeapDump
  }

//生成通知栏消息
  val notificationManager =
    context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  if (Notifications.canShowNotification) {
    val dumpingHeap = context.getString(R.string.leak_canary_notification_dumping)
    val builder = Notification.Builder(context)
      .setContentTitle(dumpingHeap)
    val notification = Notifications.buildNotification(context, builder, LEAKCANARY_LOW)
    notificationManager.notify(R.id.leak_canary_notification_dumping_heap, notification)
  }

  val toast = waitingForToast.get()

  return try {
  //测量dump耗时
    val durationMillis = measureDurationMillis {
    //将data dump到指定文件中
      Debug.dumpHprofData(heapDumpFile.absolutePath)
    }
    //文件长度为0,表明没有数据
    if (heapDumpFile.length() == 0L) {
      SharkLog.d { "Dumped heap file is 0 byte length" }
      NoHeapDump
    } else {
    //存在数据,说明dump成功,同时记录耗时
      HeapDump(file = heapDumpFile, durationMillis = durationMillis)
    }
  } catch (e: Exception) {
    SharkLog.d(e) { "Could not dump heap" }
    // Abort heap dump
    NoHeapDump
  } finally {
    cancelToast(toast)
    notificationManager.cancel(R.id.leak_canary_notification_dumping_heap)
  }
}

整体来看 AndroidHeapDumper dumpheap 方法先是创建一个 dump 的file,用于保存堆数据,然后就是通知栏的一些通知的创建, 最后就是调用  Debug.dumpHprofData(heapDumpFile.absolutePath) 来进行堆数据的转储到Android文件系统上的.hprof文件中。

下面可以看下该文件创建的代码,如下所示:

fun newHeapDumpFile(): File? {
  cleanupOldHeapDumps()

  var storageDirectory = externalStorageDirectory()
  if (!directoryWritableAfterMkdirs(storageDirectory)) {
    if (!hasStoragePermission()) {
      if (requestExternalStoragePermission()) {
        SharkLog.d { "WRITE_EXTERNAL_STORAGE permission not granted, requesting" }
        requestWritePermissionNotification()
      } else {
        SharkLog.d { "WRITE_EXTERNAL_STORAGE permission not granted, ignoring" }
      }
    } else {
      val state = Environment.getExternalStorageState()
      if (Environment.MEDIA_MOUNTED != state) {
        SharkLog.d { "External storage not mounted, state: $state" }
      } else {
        SharkLog.d {
          "Could not create heap dump directory in external storage: [${storageDirectory.absolutePath}]"
        }
      }
    }
    // Fallback to app storage.
    storageDirectory = appStorageDirectory()
    if (!directoryWritableAfterMkdirs(storageDirectory)) {
      SharkLog.d {
        "Could not create heap dump directory in app storage: [${storageDirectory.absolutePath}]"
      }
      return null
    }
  }

最终会创建一个年月日时分秒的.hprof 文件。

 Debug 是 android 系统自带的方法,最终也会调用 VMDebug 来实现,这个其实就是虚拟机的了。

public static void dumpHprofData(String fileName) throws IOException {
    VMDebug.dumpHprofData(fileName);
}

前文提到的两种结果,其实都是继承自 DumpHeapResult,其中 HeapDump 的数据结构如下:

internal data class HeapDump(
  val file: File,
  val durationMillis: Long
) : DumpHeapResult()

分析堆

当堆数据成功转储,就是对 hprof 文件的分析了。Leakcanary2.0版本开源了自己实现的 hprof 文件解析以及泄漏引用链查找的功能模块(命名为shark)。

分析hprof文件的工作主要是在 HeapAnalyzerService 类中完成的。

fun runAnalysis(
  context: Context,
  heapDumpFile: File,
  heapDumpDurationMillis: Long? = null,
  heapDumpReason: String = "Unknown"
) {
  val intent = Intent(context, HeapAnalyzerService::class.java)
  intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
  intent.putExtra(HEAPDUMP_REASON_EXTRA, heapDumpReason)
  heapDumpDurationMillis?.let {
    intent.putExtra(HEAPDUMP_DURATION_MILLIS_EXTRA, heapDumpDurationMillis)
  }
  startForegroundService(context, intent)
}

可以看到这里启动了一个后台service 来对数据进行解析。直接查看HeapAnalyzerService内的analyzeHeap方法

private fun analyzeHeap(
  heapDumpFile: File,
  config: Config
): HeapAnalysis {
  val heapAnalyzer = HeapAnalyzer(this)

  val proguardMappingReader = try {
    ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
  } catch (e: IOException) {
    null
  }
  return heapAnalyzer.analyze(
    heapDumpFile = heapDumpFile,
    leakingObjectFinder = config.leakingObjectFinder,
    referenceMatchers = config.referenceMatchers,
    computeRetainedHeapSize = config.computeRetainedHeapSize,
    objectInspectors = config.objectInspectors,
    metadataExtractor = config.metadataExtractor,
    proguardMapping = proguardMappingReader?.readProguardMapping()
  )
}

再进入HeapAnalyzer内的analyze方法

fun analyze(
  heapDumpFile: File,
  leakingObjectFinder: LeakingObjectFinder,
  referenceMatchers: List<ReferenceMatcher> = emptyList(),
  computeRetainedHeapSize: Boolean = false,
  objectInspectors: List<ObjectInspector> = emptyList(),
  metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP,
  proguardMapping: ProguardMapping? = null
): HeapAnalysis {
  val analysisStartNanoTime = System.nanoTime()

//判断hprof文件是否存在
  if (!heapDumpFile.exists()) {
    val exception = IllegalArgumentException("File does not exist: $heapDumpFile")
    return HeapAnalysisFailure(
      heapDumpFile = heapDumpFile,
      createdAtTimeMillis = System.currentTimeMillis(),
      analysisDurationMillis = since(analysisStartNanoTime),
      exception = HeapAnalysisException(exception)
    )
  }

  return try {
    listener.onAnalysisProgress(PARSING_HEAP_DUMP)
    //捕获IO读取度量,而不使用太多内存
    val sourceProvider = ConstantMemoryMetricsDualSourceProvider(FileSourceProvider(heapDumpFile))
    //从DualSourceProvider中打开CloseableHeapGraph
    sourceProvider.openHeapGraph(proguardMapping).use { graph ->
    //封装到FindLeakInput类
      val helpers =
        FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
        //FindLeakInput分析graph返回HeapAnalysisSuccess(堆分析成功)类
      val result = helpers.analyzeGraph(
        metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
      )
      val lruCacheStats = (graph as HprofHeapGraph).lruCacheStats()
      val randomAccessStats =
        "RandomAccess[" +
          "bytes=${sourceProvider.randomAccessByteReads}," +
          "reads=${sourceProvider.randomAccessReadCount}," +
          "travel=${sourceProvider.randomAccessByteTravel}," +
          "range=${sourceProvider.byteTravelRange}," +
          "size=${heapDumpFile.length()}" +
          "]"
      //为堆分析成功类增加metaData等信息
      val stats = "$lruCacheStats $randomAccessStats"
      result.copy(metadata = result.metadata + ("Stats" to stats))
    }
  } catch (exception: Throwable) {
    HeapAnalysisFailure(
      heapDumpFile = heapDumpFile,
      createdAtTimeMillis = System.currentTimeMillis(),
      analysisDurationMillis = since(analysisStartNanoTime),
      exception = HeapAnalysisException(exception)
    )
  }
}

主要是将泄露的保留对象的堆转储文件(heapDumpFile)封装一下再进行分析(helpers.analyzeGraph),分析成功返回HeapAnalysisSuccess类的实例

private fun FindLeakInput.analyzeGraph(
  metadataExtractor: MetadataExtractor,
  leakingObjectFinder: LeakingObjectFinder,
  heapDumpFile: File,
  analysisStartNanoTime: Long
): HeapAnalysisSuccess {
  listener.onAnalysisProgress(EXTRACTING_METADATA)
  val metadata = metadataExtractor.extractMetadata(graph)

  val retainedClearedWeakRefCount = KeyedWeakReferenceFinder.findKeyedWeakReferences(graph)
    .filter { it.isRetained && !it.hasReferent }.count()

  // This should rarely happens, as we generally remove all cleared weak refs right before a heap
  // dump.
  val metadataWithCount = if (retainedClearedWeakRefCount > 0) {
    metadata + ("Count of retained yet cleared" to "$retainedClearedWeakRefCount KeyedWeakReference instances")
  } else {
    metadata
  }

  listener.onAnalysisProgress(FINDING_RETAINED_OBJECTS)
  val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)

  val (applicationLeaks, libraryLeaks, unreachableObjects) = findLeaks(leakingObjectIds)

  return HeapAnalysisSuccess(
    heapDumpFile = heapDumpFile,
    createdAtTimeMillis = System.currentTimeMillis(),
    analysisDurationMillis = since(analysisStartNanoTime),
    metadata = metadataWithCount,
    applicationLeaks = applicationLeaks,
    libraryLeaks = libraryLeaks,
    unreachableObjects = unreachableObjects
  )
}

可以看到分析成功HeapAnalysisSuccess包括堆转储文件,创建时间,堆转储时长,堆分析时长,元数据信息以及应用程序泄露库泄露,这里可以看到LeakCanary将应用中的泄露分类了。