代码编织梦想

在使用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/

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/qqqq245425070/article/details/111088678

Android - 秒懂TCP连接的三次握手、四次挥手-爱代码爱编程

背景 在涉及网络知识时总是记不太清相关概念,因此期望通过简短的文字描述,理解并记住相关概念。 定义 Http 协议是在 TCP 协议基础上封装的应用层协议。 所以它在建立连接的时候会经历三次握手,断开连接会经历四次挥手。 相关标识 SYN 表示建立连接,FIN 表示关闭连接,ACK 表示响应,PSH 表示有 DATA数据传输,RST 表示连接重置

化整为零 -- Android 插件化 (概述)-爱代码爱编程

记得前几年在前一家公司上班,我们做项目的时候经常会报65535的问题,这是个很出名的问题,我记得那时候很多人外面面试的时候都会问到如何解决65535的问题,那首先了解下这是个什么问题。 在我们平时开发的Android 应用,一个app所遇到的代码都打包在一个dex文件里,这个dex文件是一个类似于Jar包那样的存储了很多有Java编译字节码的归档文件。我

Android - 秒懂TCP_UDP_IP_Socket-爱代码爱编程

背景 在涉及网络知识时总是记不太清相关概念,因此期望通过简短的文字描述,理解并记住相关概念。 TCP 属于七层协议中的传输层,是面向连接的协议。 面向连接意思就是通信双方建立连接才能通信,没建立连接不能通信。 因此是安全的。 安全可以指:能够知道通信双方,也可以指数据能够保证按顺序收到。 UDP 属于七层协议中的传输层,是面向无连接的协议。

排序算法复习(上篇)-爱代码爱编程

排序算法概述 排序就是将一组对象按照某种逻辑顺序重新排列的过程。比如,订单按照日期排序的——这种排序很可能使用了某种排序算法。在计算时代早期,大家普遍认为30% 的计算周期都用在了排序上。如果今天这个比例降低了,可能的原因之一是如今的排序算法更加高效,而并非排序的重要性降低了。现在计算机的广泛使用使得数据无处不在,而整理数据的第一步通常就是进行排序。几乎

Fragment的传值应用-爱代码爱编程

Fragment高级应用 Fragment的传值activity给fragment传值fragment给activity传值fragment给fragment传值 Fragment的传值 不同页面之间的传值是最基本的要求 activity给fragment传值 getArguments()和setArguments() 一

叶子相似的树(Java)-爱代码爱编程

考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个叶值序列 如上图所示,给定一棵叶值序列为 (6, 7, 4, 9, 8) 的树。 如果有两棵二叉树的叶值序列是相同,那么我们就认为它们是叶相似的。 如果给定的两个头结点分别为 root1 和 root2 的树是叶相似的,则返回 true;否则返回 false 。 示例 1: 输

死磕Android_LeakCanary原理赏析-爱代码爱编程

本文基于 leakcanary-android:2.5 我的所有原创Android知识体系,已打包整理到GitHub.努力打造一系列适合初中高级工程师能够看得懂的优质文章,欢迎star~ 思维导图 1. 背景 Android开发中,内存泄露时常有发生在,有可能是你自己写的,也有可能是三方库里面的.程序中已动态分配的堆内存由于某种特殊原

Android 主流开源框架(九)LeakCanary 源码解析-爱代码爱编程

前言 最近有个想法——就是把 Android 主流开源框架进行深入分析,然后写成一系列文章,包括该框架的详细使用与源码解析。目的是通过鉴赏大神的源码来了解框架底层的原理,也就是做到不仅要知其然,还要知其所以然。 这里我说下自己阅读源码的经验,我一般都是按照平时使用某个框架或者某个系统源码的使用流程入手的,首先要知道怎么使用,然后再去深究每一步底层做了什

LeakCanary浅析-内存泄漏对象检测-爱代码爱编程

下面内容都是基于LeakCanary2.5 使用 根据官方文档的说明,只需在build.gradle中导入依赖com.squareup.leakcanary:leakcanary-android:xx即可 dependencies { // debugImplementation because LeakCanary should only ru