LeakCanary简单分析

在使用LeakCanary的时候要引入:

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

debugImplementation  : debugImplementation 只在debug模式的编译和最终的debug apk打包时有效

LeakCanary的初始化是利用ContentProvider进行初始化的,

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

ContentProvider是在Application的onCreate的前面执行的,也就是在App启动的时候已经初始化好了。具体的ContentProvider是在什么时候执行的可以参考博客

下面是AppWatcherInstaller的ContentProvider的onCreate代码:

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

AppWatcher是一个单例类只要作用:

The entry point API for using [ObjectWatcher] in an Android app. [AppWatcher.objectWatcher] is
* in charge of detecting retained objects, and [AppWatcher] is auto configured on app start to
* pass it activity and fragment instances. Call [ObjectWatcher.watch] on [objectWatcher] to
* watch any other object that you expect to be unreachable.

在Android应用中使用[ObjectWatcher]的入口点API。 *[AppWatcher.objectWatcher]负责检测保留的对象,[AppWatcher]在应用程序启动时自动配置为通过它的Activity和Fragment实例。

调用[objectWatcher]上的[ObjectWatcher.watch]以监视您期望无法访问的任何其他对象。

  /**
   * [AppWatcher] is automatically installed in the main process on startup. You can
   * disable this behavior by overriding the `leak_canary_watcher_auto_install` boolean resource:
   *
   * ```
   * <?xml version="1.0" encoding="utf-8"?>
   * <resources>
   *   <bool name="leak_canary_watcher_auto_install">false</bool>
   * </resources>
   * ```
   *
   * If you disabled automatic install then you can call this method to install [AppWatcher].
   */
  fun manualInstall(application: Application) {
    InternalAppWatcher.install(application)
  }

这是在values.xml中的一个字段
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="leak_canary_watcher_auto_install">true</bool>
</resources>

下面就是install的代码:

 fun install(application: Application) {
    checkMainThread()//检查是否在主线程,如果不是抛出异常
    if (this::application.isInitialized) {//判断这个lateinit的属性是否被复制
      return
    }
    SharkLog.logger = DefaultCanaryLog()//设置日志打印
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }//设置config
    //下面这两个就是观察Activity和Fragment是否泄漏的
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
  }
data class Config(
    /**
     * Whether AppWatcher should automatically watch destroyed activity instances.
     *
     * Defaults to true.
     */
    val watchActivities: Boolean = true,

    /**
     * Whether AppWatcher should automatically watch destroyed fragment instances.
     *
     * Defaults to true.
     */
    val watchFragments: Boolean = true,

    /**
     * Whether AppWatcher should automatically watch destroyed fragment view instances.
     *
     * Defaults to true.
     */
    val watchFragmentViews: Boolean = true,

    /**
     * Whether AppWatcher should automatically watch cleared [androidx.lifecycle.ViewModel]
     * instances.
     *
     * Defaults to true.
     */
    val watchViewModels: Boolean = true,

    /**
     * How long to wait before reporting a watched object as retained.
     *
     * Default to 5 seconds.
     */
    val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5),

    /**
     * Deprecated, this didn't need to be a part of the API.
     * Used to indicate whether AppWatcher should watch objects (by keeping weak references to
     * them). Currently a no-op.
     *
     * If you do need to stop watching objects, simply don't pass them to [objectWatcher].
     */
    @Deprecated("This didn't need to be a part of LeakCanary's API. No-Op.")
    val enabled: Boolean = true
  )

下面我们先分析这个Activity的写泄漏:主要是给application的registerActivityLifecycleCallbacks添加一个监听,

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)
    }
  }
}

下面就是在回调了Activity的onDestroy的方法后执行的内容:这个地方就是创建一个弱引用对象,进行观察,当执行完GC之后是否还存在,判断是否泄漏

/**
   * Watches the provided [watchedObject].
   *
   * @param description Describes why the object is watched.
   */
  @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)
    }
  }

 


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

 这个地方的onObjectRetained()方法是InternalLeakCanary中的onObjectRetained()方法可以看到调用的时候HeapDumpTrigger对象的onObjectRetained()方法下面看下这个方法

fun onObjectRetained() {
    scheduleRetainedObjectCheck(
        reason = "found new object retained",
        rescheduling = false
    )
  }
  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
    }
       //弱引用对象中保存的没有回收的Activity的数量
    var retainedReferenceCount = objectWatcher.retainedObjectCount

    if (retainedReferenceCount > 0) {//数量大于0执行GC,执行GC后再获取一次个数
      gcTrigger.runGc()
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }
    //这个地方是检查回收对象的数量是否超过定义的阀值(5)如果没有就直接返回
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

    //判断是否连接了调试解调器,如果是就直接返回
    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
      onRetainInstanceListener.onEvent(DebuggerIsAttached)
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_debugger_attached
          )
      )
      scheduleRetainedObjectCheck(
          reason = "debugger is attached",
          rescheduling = true,
          delayMillis = WAIT_FOR_DEBUG_MILLIS
      )
      return
    }

    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    //如果时间小于预定义的5秒就Notification并且返回
    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)
  }
private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean
  ) {
    saveResourceIdNamesToMemory()
    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
    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)
    //保存到文件后分析这个hprof文件
    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
  }
fun runAnalysis(
      context: Context,
      heapDumpFile: File
    ) {
        //启动一个前台服务分析堆文件
      val intent = Intent(context, HeapAnalyzerService::class.java)
      intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
      startForegroundService(context, intent)
    }

下面就是hprof文件的分析了,LeakCanary用的Shark进行分析的。shark官网

LeakCanary的官网

LeakCanary自动检测以下对象的泄漏:

  • 销毁Activity实例
  • 销毁Fragment实例
  • destroy fragment View实例
  • 清除ViewModel实例

它就会通过4个步骤自动检测并报告内存泄漏:

  1. 检测保留的对象。
  2. 转储堆。
  3. 分析堆。
  4. 分类泄漏。

详细的流程可以去官网看下,地址为https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值