LeakCanary 2.4 Kotlin源码解析

1,616 阅读4分钟

LeakCanary 2.4 Kotlin源码

1. 初始化

新的LeakCanary使用kotlin开发,而且不需要在Application中添加LeakCanary.install(this)进行初始化。那么究竟是用什么方式能够跟随APP启动进行自动初始化的呢?

在leakcanary-object-watcher-android module的 manifest中看到了:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.squareup.leakcanary.objectwatcher"
    >

  <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$MainProcess类

/**
 * Content providers are loaded before the application class is created. [AppWatcherInstaller] is
 * used to install [leakcanary.AppWatcher] on application start.
 */
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
  }
}

AppWatchInstaller是一个继承了ContentProvider的密封类。关于密封类的解释:www.kotlincn.net/docs/refere… 。我理解就像是个可以有多个不同实例的枚举类型。MainProcess和LeakCanaryProcess两个子类分别用于不同的进程中启动。

AppWatchInstaller中实现的ContentProvider的query, getType, insert, delete, update等方法都只是个空实现,返回null。很明显这个ContentProvider是个不务正业的数据提供者,他不是为了提供数据,而是要在APP启动时,执行AppWatcher.manualInstall(application)

AppWather调用InternalAppWatcher.install(application)

  fun manualInstall(application: Application) {
    InternalAppWatcher.install(application)
  }

InternalAppWatcher:

  fun install(application: Application) {
    checkMainThread()
    if (this::application.isInitialized) {
      return
    }
    SharkLog.logger = DefaultCanaryLog()
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
  }
  1. 检查是否在主线程
  2. 检查application是否已经赋值
  3. ActivityDestroyWatcher.install
  4. FragmentDestroyWatcher.install
  5. install完成后要做的的动作

ActivityDestroyWatcher.install的方法在ActivityDestroyWatcher的companion object中,相当于Java中的静态方法

internal class ActivityDestroyWatcher private constructor(
  private val objectWatcher: ObjectWatcher,
  private val configProvider: () -> Config
) {

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

  companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
  }
}

在install方法中通过application.registerActivityLifecycleCallbacks()注册了Activity生命周期的监听,调用ObjectWatcher.watch()方法开始监测Activity。

FragmentDestroyWatcher是一个internal object。internal 表示可见范围是module内,object是对象声明,其实就是一个单例。代码如下:

internal object FragmentDestroyWatcher {

  private const val ANDROIDX_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"
  private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
    "leakcanary.internal.AndroidXFragmentDestroyWatcher"

  // Using a string builder to prevent Jetifier from changing this string to Android X Fragment
  @Suppress("VariableNaming", "PropertyName")
  private val ANDROID_SUPPORT_FRAGMENT_CLASS_NAME =
    StringBuilder("android.").append("support.v4.app.Fragment")
        .toString()
  private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
    "leakcanary.internal.AndroidSupportFragmentDestroyWatcher"

  fun install(
    application: Application,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
  ) {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
          AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
      )
    }

    getWatcherIfAvailable(
        ANDROIDX_FRAGMENT_CLASS_NAME,
        ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      fragmentDestroyWatchers.add(it)
    }

    getWatcherIfAvailable(
        ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
        ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      fragmentDestroyWatchers.add(it)
    }

    if (fragmentDestroyWatchers.size == 0) {
      return
    }

    application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        for (watcher in fragmentDestroyWatchers) {
          watcher(activity)
        }
      }
    })
  }

  private fun getWatcherIfAvailable(
    fragmentClassName: String,
    watcherClassName: String,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
  ): ((Activity) -> Unit)? {

    return if (classAvailable(fragmentClassName) &&
        classAvailable(watcherClassName)
    ) {
      val watcherConstructor = Class.forName(watcherClassName)
          .getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java)
      @Suppress("UNCHECKED_CAST")
      watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit

    } else {
      null
    }
  }

  private fun classAvailable(className: String): Boolean {
    return try {
      Class.forName(className)
      true
    } catch (e: Throwable) {
      // e is typically expected to be a ClassNotFoundException
      // Unfortunately, prior to version 25.0.2 of the support library the
      // FragmentManager.FragmentLifecycleCallbacks class was a non static inner class.
      // Our AndroidSupportFragmentDestroyWatcher class is compiled against the static version of
      // the FragmentManager.FragmentLifecycleCallbacks class, leading to the
      // AndroidSupportFragmentDestroyWatcher class being rejected and a NoClassDefFoundError being
      // thrown here. So we're just covering our butts here and catching everything, and assuming
      // any throwable means "can't use this". See https://github.com/square/leakcanary/issues/1662
      false
    }
  }
}

如果系统是Android O以后版本,使用AndroidOFragmentDestroyWatcher,如果app使用的是androidx中的fragment,则添加对应的AndroidXFragmentDestroyWatcher,如果使用support库中的fragment,则添加AndroidSupportFragmentDestroyWatcher。

然后注册Activity生命周期监听,在onActivityCreated时开始监听Activity的fragment。

这里的xxWatcher都继承(Activity) -> Unit,啥意思呢?这些Watcher是 方法类型!就是说他们是个方法,方法参数为Activity类型,没有返回值。所以他们都要重写invoke方法。下面是AndroidOFragmentDestroyWatcher的invoke方法:

  override fun invoke(activity: Activity) {
    val fragmentManager = activity.fragmentManager
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }

其他几个FragmentDestroyWatcher的invoke()方法都差不多,使用FragmentManager或SupportFragmentManager.registerFragmentLifecycleCallbacks()注册监听。不过具体监听方式有所不同。稍后再讲。

Activity和Fragment对应的watcher 在 install之后就是onAppWatcherInstalled(application)

onAppWatcherInstalled也是个方法类型,参数为Application,没有返回值。

InternalAppWatcher中的初始化代码块就是onAppWatcherInstalled赋值的地方。这个初始化代码块相当于Java中静态类中的static代码块。

init {
    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 as (Application) -> Unit
}

这里onAppWatcherInstalled赋值到InternalLeakCanary对象。我们来到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 }

    val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
    handlerThread.start()
    val backgroundHandler = Handler(handlerThread.looper)

    heapDumpTrigger = HeapDumpTrigger(
        application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
        configProvider
    )
    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    registerResumedActivityListener(application)
    addDynamicShortcut(application)

    disableDumpHeapInTests()
  }
  • heapDumper 用于确认内存泄漏之后进行 heap dump 工作。
  • gcTrigger 用于发现可能的内存泄漏之后手动调用 GC 确认是否真的为内存泄露。

这两个对象是 LeakCanary 检测内存泄漏的核心。后面会进行详细分析。到这里,整个 LeakCanary 的初始化工作就完成了。

2. ObjectWatcher.watch()

  private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()

  private val queue = ReferenceQueue<Any>()

  @Synchronized fun watch(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      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"
    }

    watchedObjects[key] = reference
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }
  1. 调用removeWeaklyReachableObjects(),移除watchedObjects中将被GC的引用。
  2. 将要观察的对象生成KeyedWeakReference并加入到watchedObjects中
  3. 使用checkRetainedExecutor定时执行moveToRetained()

调用removeWeaklyReachableObjects方法,多次从queue中取出已回收的对象,并从watchedObject中删除

  private fun removeWeaklyReachableObjects() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }

moveToRetained(key: String) 方法,再次移除watchedObjects中将被GC的引用,然后取出未被回收的retainedRef,执行onObjectRetainedListeners.onObjectRetained()

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

onObjectRetainedListeners.onObjectRetained()最后会回到InternalLeakCanary.onObjectRetained(),并且在里面调用HeapDumpTrigger.onObjectRetained(),然后是

private fun scheduleRetainedObjectCheck(
    reason: String,
    rescheduling: Boolean,
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
      SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" }
      return
    } else {
      val verb = if (rescheduling) "Rescheduling" else "Scheduling"
      val delay = if (delayMillis > 0) " in ${delayMillis}ms" else ""
      SharkLog.d { "$verb check for retained objects${delay} because $reason" }
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      checkRetainedObjects(reason)
    }, delayMillis)
  }

最后就是通过checkRetainedObjects(reason)检查是否泄漏

  private fun checkRetainedObjects(reason: String) {
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
      SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
      return
    }

    var retainedReferenceCount = objectWatcher.retainedObjectCount

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

    // 泄漏对象数小于5个,不进行dump,只给弹通知。
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

    // 如果正在debug,不进行dump,因为debug可能会引起临时的内存泄漏(比如被断点block的线程)
    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
      onRetainInstanceListener.onEvent(DebuggerIsAttached)
      // 显示相关通知和提示
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_debugger_attached
          )
      )
      // 等待20秒后重新检查
      scheduleRetainedObjectCheck(
          reason = "debugger is attached",
          rescheduling = true,
          delayMillis = WAIT_FOR_DEBUG_MILLIS
      )
      return
    }

    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    // 距上次dump小于60秒,等待够了60秒后再检查
    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(
          reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
          rescheduling = true,
          delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
      )
      return
    }

    SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
    dismissRetainedCountNotification()
    dumpHeap(retainedReferenceCount, retry = true)
  }

在经过一系列的条件判定之后,进行dumpHeap。

  private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean
  ) {
    // 资源id和name对应起来
    saveResourceIdNamesToMemory()
    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
    // dump文件
    val heapDumpFile = heapDumper.dumpHeap()
    if (heapDumpFile == null) {
      if (retry) {
        SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
        scheduleRetainedObjectCheck(
            reason = "failed to dump heap",
            rescheduling = true,
            delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
        )
      } else {
        SharkLog.d { "Failed to dump heap, will not automatically retry" }
      }
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_dump_failed
          )
      )
      return
    }
    lastDisplayedRetainedObjectCount = 0
    lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
    objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
    // 分析
    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
  }

HeapAnalyzerService.runAnalysis(application, heapDumpFile)启动一个前台服务来进行dump文件分析。现在不再使用haha的库来解析,而是改用自己的一套shark库解析。具体内容见链接:square.github.io/leakcanary/…

新的LeakCanary主要有以下不同

  1. 百分之百使用 Kotlin 重写
  2. 自动初始化,无需用户手动再添加初始化代码
  3. 支持Fragment和androidx
  4. 当泄露引用到达 5 个时才会发起 heap dump
  5. 全新的内存解析方法,减少 90% 内存占用,提升 6 倍速度

首次源码解析,有的地方可能理解错误,还请多多指正!