Leakcanary一篇就够

一.模块依赖关系

 

 

 

检测流程图

ui显示流程图

leakcanary-android-release流程图

 

二.源码解析

1. leakcanary-android-utils

1.1 Handlers.kt 主线程handler获取,判断是否是主线程

//获取主线程,by lazy是kotlin懒加载赋值,调用的时候才赋值
internal val mainHandler by lazy { Handler(Looper.getMainLooper()) }

//是否处于主线程
internal fun checkMainThread() {
  check(Looper.getMainLooper().thread === Thread.currentThread()) {//如果不满足,报IllegalStateException
    "Should be called from the main thread, not ${Thread.currentThread()}"
  }
}

//是否不处于主线程
internal fun checkNotMainThread() {
  check(Looper.getMainLooper().thread !== Thread.currentThread()) {
    "Should not be called from the main thread"
  }
}

1.2 Objects.kt 动态代理接口

//动态代理相关的
//https://blog.csdn.net/u012326462/article/details/81293186
//newProxyInstance,方法有三个参数:
//ClassLoader loader: 用哪个类加载器去加载代理对象
//Class<?>[] interfaces:动态代理类需要实现的接口
//InvocationHandler h:动态代理方法在执行时,会调用h里面的invoke方法去执行
//
//invoke三个参数:
//proxy:就是代理对象,newProxyInstance方法的返回对象
//method:调用的方法
//args: 方法中的参数
internal inline fun <reified T : Any> noOpDelegate(): T {
  //  https://blog.csdn.net/lv_fq/article/details/72869124
  //  双冒号 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法
  //  https://blog.csdn.net/lmo28021080/article/details/81505211?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5.control
  //  val a = Test::class                 // KClass
  //  val b1 = Test::class.java           // Class (KClass -> Class)
  //  val b2 = Test::class.java.kotlin    // KClass (Class -> KClass)
  val javaClass = T::class.java
  return Proxy.newProxyInstance(
    javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER
  ) as T
}

//这里是koltin的函数表达式
private val NO_OP_HANDLER = InvocationHandler { _, _, _ ->
  // no op
}

1.3 Timing.kt 计算代码块运行时间

//计算代码块运行时间
internal inline fun measureDurationMillis(block: () -> Unit): Long {
  val start = SystemClock.uptimeMillis()
  block()
  return SystemClock.uptimeMillis() - start
}

1.4 LogcatSharkLog 日志类

class LogcatSharkLog : Logger {

  override fun d(message: String) {
    if (message.length < 4000) {
      Log.d("LeakCanary", message)
    } else {
      message.lines().forEach { line ->
        Log.d("LeakCanary", line)
      }
    }
  }

  override fun d(
    throwable: Throwable,
    message: String
  ) {
    d("$message\n${Log.getStackTraceString(throwable)}")
  }

  //https://www.jianshu.com/p/14db81e1576a
  companion object {//伴生对象
    //  companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。
    //  调用方法 LogcatSharkLog.install()
    fun install() {
      SharkLog.logger = LogcatSharkLog()
    }
  }
}

2.leakcanary-object-watcher

2.1 Clock  uptimeMillis用于获取自开机后,经过的时间,不包括深度睡眠的时间

fun interface Clock {//https://www.zhihu.com/question/362356051 fun interface表示单个抽象函数
  /**
   * On Android VMs, this should return android.os.SystemClock.uptimeMillis().
   */
  fun uptimeMillis(): Long

  companion object {//伴生对象
    /**
     * Utility function to create a [Clock] from the passed in [block] lambda
     * instead of using the anonymous `object : Clock` syntax.
     *
     * 从传入的[block]lambda创建[Clock]的实用函数
     * 而不是使用匿名的`object:Clock`语法。
     *
     * Usage:
     *
     * ```kotlin
     * val clock = Clock {
     *
     * }
     * ```
     */
    //内联函数 https://blog.csdn.net/xlh1191860939/article/details/86736379
    //当你调用一个inline function的时候,编译器不会产生一次函数调用,而是会在每次调用时,将inline function中的代码直接嵌入到调用处。
    //crossinline https://blog.csdn.net/xlh1191860939/article/details/105228583
    //crossinline “non-local returns”,非局部返回,这里是限制在内联函数中出现return局部返回

    //operator https://blog.csdn.net/qq_34589749/article/details/103643764
    //operator 操作符 Kotlin-14-运算符重载(operator)
    //https://blog.csdn.net/chzphoenix/article/details/78094584
    //
    //invoke
    //调用invoke时方法可以被省略,所以如果
    //class ClickAction{
    //     operator fun invoke(...){
    //          ...
    //     }
    //}
    //可以直接:
    //clickAction(...)
    //注意clickAction是类的一个对象
    //invoke 方法,Lambda 表达式调用位置 https://blog.csdn.net/mlsnatalie/article/details/88557502

    //使用地方
	//AppWatcher 构造函数 clock = { SystemClock.uptimeMillis() },
	//val watchUptimeMillis = clock.uptimeMillis()
    inline operator fun invoke(crossinline block: () -> Long): Clock =
      object : Clock {
        override fun uptimeMillis(): Long = block()
      }
  }
}

2.2 GcTrigger 触发GC方法

todo放流程图

/**
 * [GcTrigger] is used to try triggering garbage collection and enqueuing [KeyedWeakReference] into
 * the associated [java.lang.ref.ReferenceQueue]. The default implementation [Default] comes from
 * AOSP Android 开源项目(AOSP) .
 * ReferenceQueue 引用队列
 */
interface GcTrigger {

  /**
   * Attempts to run garbage collection.
   */
  fun runGc()

  /**
   * Default implementation of [GcTrigger].
   */
  //object 静态类,里边的方法,参数都是静态的
  object Default : GcTrigger {
    override fun runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perform a gc.
      Runtime.getRuntime()
        .gc()
      enqueueReferences()
      System.runFinalization()
    }

    private fun enqueueReferences() {
      // Hack(黑客). We don't have a programmatic way to wait for the reference queue daemon(守护进程) to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100)
      } catch (e: InterruptedException) {
        throw AssertionError()
      }
    }
  }
}

2.3 KeyedWeakReference弱引用包装类

todo放流程图

//https://www.jianshu.com/p/73260a46291c
//对于软引用和弱引用,我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列了。
//ReferenceQueue即这样的一个对象,当一个obj被gc掉之后,其相应的包装类,即ref对象会被放入queue中。
//我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。
class KeyedWeakReference(
  referent: Any,
  val key: String,
  val description: String,
  val watchUptimeMillis: Long,
  referenceQueue: ReferenceQueue<Any>//
) : WeakReference<Any>(
  referent, referenceQueue
) {
  /**
   * Time at which the associated object ([referent]) was considered retained, or -1 if it hasn't
   * been yet.
   */
  @Volatile
  var retainedUptimeMillis = -1L//观察开始的时间

  override fun clear() {
    super.clear()
    retainedUptimeMillis = -1L
  }

  companion object {//伴生对象,静态对象,他的都是静态值,一个类只能有一个伴生对象
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L//dump开始的时间
  }
}

2.4 ReachabilityWatcher接口,开启观察

todo放流程图

fun interface ReachabilityWatcher {

  /**
   * Expects the provided [watchedObject] to become weakly reachable soon. If not,
   * [watchedObject] will be considered retained.
   *
   * 期望提供的[watchedObject]很快变得弱可访问。如果没有,
   * [watchedObject]将被视为保留
   */
  fun expectWeaklyReachable(
    watchedObject: Any,//需要检测的对象,任意
    description: String//描述
  )
}

2.5 OnObjectRetainedListener对象泄漏的调用接口

todo放流程图

fun interface OnObjectRetainedListener {//单一函数

  /**
   * A watched object became retained.
   */
  fun onObjectRetained()//单一函数

  companion object {//伴生对象
    /**
     * Utility function to create a [OnObjectRetainedListener] from the passed in [block] lambda
     * instead of using the anonymous `object : OnObjectRetainedListener` syntax.
     *
     * Usage:
     *
     * ```kotlin
     * val listener = OnObjectRetainedListener {
     *
     * }
     * ```
     */
    inline operator fun invoke(crossinline block: () -> Unit): OnObjectRetainedListener =
      object : OnObjectRetainedListener {
        override fun onObjectRetained() {
          block()
        }
      }
  }
}

2.5 ObjectWatcher保持一个map存放观察的对象,创建ReferenceQueue保存已被回收的对象的弱饮用包装类对象,expectWeaklyReachable开启观察

todo放流程图

class ObjectWatcher constructor(
  private val clock: Clock,                   //时间
  private val checkRetainedExecutor: Executor,//线程池
  /**
   * Calls to [watch] will be ignored when [isEnabled] returns false
   */
  private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
  //接口,mutable可变的 https://blog.csdn.net/xsg2357/article/details/80417980
  private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()

  /**
   * References passed to [watch].
   */
  //存放在观察的实例的key,和KeyedWeakReference
  private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()//

  //ReferenceQueue里边存放已被回收的对象的包装类
  private val queue = ReferenceQueue<Any>()//队列,存放KeyedWeakReference

  //region hasRetainedObjects,retainedObjectCount,hasWatchedObjects,retainedObjects
  /**
   * Returns true if there are watched objects that aren't weakly reachable, and
   * have been watched for long enough to be considered retained.
   */
  val hasRetainedObjects: Boolean
    @Synchronized get() {
      removeWeaklyReachableObjects()
      return watchedObjects.any { it.value.retainedUptimeMillis != -1L }
    }

  /**
   * Returns the number of retained objects, ie the number of watched objects that aren't weakly
   * reachable, and have been watched for long enough to be considered retained.
   */
  val retainedObjectCount: Int
    @Synchronized get() {
      removeWeaklyReachableObjects()
      return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
    }

  /**
   * Returns true if there are watched objects that aren't weakly reachable, even
   * if they haven't been watched for long enough to be considered retained.
   */
  val hasWatchedObjects: Boolean
    @Synchronized get() {
      removeWeaklyReachableObjects()
      return watchedObjects.isNotEmpty()
    }

  /**
   * Returns the objects that are currently considered retained. Useful for logging purposes.
   * Be careful with those objects and release them ASAP as you may creating longer lived leaks
   * then the one that are already there.
   */
  val retainedObjects: List<Any>
    @Synchronized get() {
      removeWeaklyReachableObjects()
      val instances = mutableListOf<Any>()
      for (weakReference in watchedObjects.values) {
        if (weakReference.retainedUptimeMillis != -1L) {
          val instance = weakReference.get()
          if (instance != null) {
            instances.add(instance)
          }
        }
      }
      return instances
    }

  @Synchronized fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) {
    onObjectRetainedListeners.add(listener)
  }

  @Synchronized fun removeOnObjectRetainedListener(listener: OnObjectRetainedListener) {
    onObjectRetainedListeners.remove(listener)
  }

  /**
   * Identical to [watch] with an empty string reference name.
   */
  @Deprecated(
    "Add description parameter explaining why an object is watched to help understand leak traces.",
    replaceWith = ReplaceWith(
      "expectWeaklyReachable(watchedObject, \"Explain why this object should be garbage collected soon\")"
    )
  )
  fun watch(watchedObject: Any) {
    expectWeaklyReachable(watchedObject, "")
  }

  @Deprecated(
    "Method renamed expectWeaklyReachable() to clarify usage.",
    replaceWith = ReplaceWith(
      "expectWeaklyReachable(watchedObject, description)"
    )
  )
  fun watch(
    watchedObject: Any,
    description: String
  ) {
    expectWeaklyReachable(watchedObject, description)
  }
  //endregion


  @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    //是否启用 , AppWatcher 持有的ObjectWatcher 默认是启用的
    if (!isEnabled()) {
      return
    }
    //移除之前已经被回收的监听对象
    removeWeaklyReachableObjects()
    val key = UUID.randomUUID()
      .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    //创建弱引用,创建要观察对象的弱引用,传入queue 作为gc 后的对象信息存储队列
    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 {
      //在子线程里,先清除回收的对象,如果此watchedObject没有被回收,则记录他泄漏检测开始的时间
      moveToRetained(key)
    }
  }

  /**
   * Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on
   * [clock] [Clock.uptimeMillis])
   *
   * 清除 heapDumpUptimeMillis之前创建的KeyedWeakReference
   */
  @Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
    val weakRefsToRemove =
      watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }
    weakRefsToRemove.values.forEach { it.clear() }
    watchedObjects.keys.removeAll(weakRefsToRemove.keys)
  }

  /**
   * Clears all [KeyedWeakReference]
   */
  @Synchronized fun clearWatchedObjects() {
//   watchedObjects 存放在观察的实例的key,和KeyedWeakReference弱引用包装对象
    //这里清空
    watchedObjects.values.forEach { it.clear() }
    watchedObjects.clear()
  }

  //在子线程里,先清除回收的对象,如果此watchedObject没有被回收,则记录他泄漏检测开始的时间
  @Synchronized private fun moveToRetained(key: String) {
    ///移除已经被回收的观察对象
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      //记录泄漏时间
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      //回调泄漏监听
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }

  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 {
      //ReferenceQueue会存放gc之后,被回收的弱饮用包装对象,
      ref = queue.poll() as KeyedWeakReference?
      if (ref != null) {
        //将此对象在watchedObjects里删除
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }
}

3.leakcanary-object-watcher-android

3.1 Friendly.kt 工具类,获取主线程相关,动态代理相关

@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "NOTHING_TO_INLINE")
@file:JvmName("leakcanary-object-watcher-android_Friendly")
//ok
package leakcanary.internal.friendly

//主线程handler
internal inline val mainHandler
  get() = leakcanary.internal.mainHandler

//检查是否是主线程
internal inline fun checkMainThread() = leakcanary.internal.checkMainThread()

//动态代理
internal inline fun <reified T : Any> noOpDelegate(): T = leakcanary.internal.noOpDelegate()

3.2 Applications.kt 判断是否DEBUGGABLE,如果debugable,就会打印log,LogcatSharkLog.install()

//判断是否DEBUGGABLE,如果debugable,就会打印log,LogcatSharkLog.install()
internal val Application.isDebuggableBuild: Boolean
  get() = (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0

3.3 InstallableWatcher 观察者接口,Activity,Fragment,View,Service监听都实现了这个接口

todo放流程图

interface InstallableWatcher {

  fun install()

  fun uninstall()
}

3.4 LeakCanaryDelegate,使用反射调用android-core模块里的InternalLeakCanary实例 

todo放流程图

internal object LeakCanaryDelegate {//代表;

  @Suppress("UNCHECKED_CAST")
  val loadLeakCanary by lazy {//通过单例的INSTANCE字段获取InternalLeakCanary实例
    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() {
    }
  }
}

3.5 AppWatcherInstaller 安装类

todo放流程图

/**
 * Content providers are loaded before the application class is created. [AppWatcherInstaller] is
 * used to install [leakcanary.AppWatcher] on application start.
 * LeakCanary利用ContentProvider的onCreate生命周期处于Application的onCreate生命周期之前的特性,
 * 将注册部分写在ContentProvider中。
 * ok
 *
 * sealed class密封类
 * https://blog.csdn.net/huo108/article/details/80544645
 * Sealed class(密封类) 是一个有特定数量子类的类,看上去和枚举有点类似,所不同的是,在枚举中,我们每个类型只有一个对象(实例);而在密封类中,同一个类可以拥有几个对象。
 * Sealed class(密封类)的所有子类都必须与密封类在同一文件中
 * Sealed class(密封类)的子类的子类可以定义在任何地方,并不需要和密封类定义在同一个文件中
 * Sealed class(密封类)没有构造函数,不可以直接实例化,只能实例化内部的子类
 */
internal sealed class AppWatcherInstaller : ContentProvider() {//sealed

  //代码中定义了两个内部类继承自 AppWatcherInstaller。当用户额外依赖 leakcanary-android-process 模块的时候,
  // 自动在 process=":leakcanary" 也注册该provider。
  /**
   * [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
  }

  override fun query(
    uri: Uri,
    strings: Array<String>?,
    s: String?,
    strings1: Array<String>?,
    s1: String?
  ): Cursor? {
    return null
  }

  override fun getType(uri: Uri): String? {
    return null
  }

  override fun insert(
    uri: Uri,
    contentValues: ContentValues?
  ): Uri? {
    return null
  }

  override fun delete(
    uri: Uri,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }

  override fun update(
    uri: Uri,
    contentValues: ContentValues?,
    s: String?,
    strings: Array<String>?
  ): Int {
    return 0
  }
}

3.6 AppWatcher 安装manualInstall

todo放流程图

object AppWatcher {
  //retained
  private const val RETAINED_DELAY_NOT_SET = -1L 

  @Volatile
  private var retainedDelayMillis = RETAINED_DELAY_NOT_SET //5s

  private var installCause: Exception? = null

  /**
   * The [ObjectWatcher] used by AppWatcher to detect retained objects.
   * Only set when [isInstalled] is true.
   * 可以看到objectWatcher 是一个 ObjectWatcher对象,该对象负责检测持有对象的泄漏情况,
   */
  val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
      mainHandler.postDelayed(it, retainedDelayMillis)//5s之后执行 todo 这里是主线程???
    },
    isEnabled = { true }
  )

  /** @see [manualInstall] */
  val isInstalled: Boolean
    get() = installCause != null

  /**
   * Enables usage of [AppWatcher.objectWatcher] which will expect passed in objects to become
   * weakly reachable within [retainedDelayMillis] ms and if not will trigger LeakCanary (if
   * LeakCanary is in the classpath).
   *
   * In the main process, this method is automatically called with default parameter values  on app
   * startup. You can call this method directly to customize the installation, however you must
   * first disable the automatic call 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>
   * ```
   *
   * [watchersToInstall] can be customized to a subset of the default app watchers:
   *
   * ```
   * val watchersToInstall = AppWatcher.appDefaultWatchers(application)
   *   .filter { it !is RootViewWatcher }
   * AppWatcher.manualInstall(
   *   application = application,
   *   watchersToInstall = watchersToInstall
   * )
   * ```
   *
   * [watchersToInstall] can also be customized to ignore specific instances (e.g. here ignoring
   * leaks of BadSdkLeakingFragment):
   *
   * ```
   * val watchersToInstall = AppWatcher.appDefaultWatchers(application, ReachabilityWatcher { watchedObject, description ->
   *   if (watchedObject !is BadSdkLeakingFragment) {
   *     AppWatcher.objectWatcher.expectWeaklyReachable(watchedObject, description)
   *   }
   * })
   * AppWatcher.manualInstall(
   *   application = application,
   *   watchersToInstall = watchersToInstall
   * )
   * ```
   * AppWatcher是初始化观察对象的入口处。
   * manualInstall方法的参数watchersToInstall默认实现Activity、Fragment、View、ViewModel和Service的监听。
   */
  @JvmOverloads
  fun manualInstall(
    application: Application,
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
    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
    //android-core模块里,使用反射调用InternalLeakCanary构造函数
    LeakCanaryDelegate.loadLeakCanary(application)

    //开启监测
    watchersToInstall.forEach {
      it.install()
    }
  }

  /**
   * Creates a new list of default app [InstallableWatcher], created with the passed in
   * [reachabilityWatcher] (which defaults to [objectWatcher]). Once installed,
   * these watchers will pass in to [reachabilityWatcher] objects that they expect to become
   * weakly reachable.
   *
   * The passed in [reachabilityWatcher] should probably delegate to [objectWatcher] but can
   * be used to filter out specific instances.
   */
  //提供了 四个默认监听的Watcher
  fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
      ActivityWatcher(application, reachabilityWatcher),
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      RootViewWatcher(reachabilityWatcher),
      ServiceWatcher(reachabilityWatcher)
    )
  }
}

3.7 ActivityWatcher监听Activity泄漏

/**
 * Expects activities to become weakly reachable soon after they receive the [Activity.onDestroy]
 * callback.
 * 通过application监听Activity的生命周期,在onDestory调用expectWeaklyReachable方法
 * 做内存泄漏的检查工作。
 *
 * 调用ActivityInstaller.install 初始化方法
 * 在Application 注册ActivityLifecycleCallbacks
 * 在所有activity onDestroy的时候调用ObjectWatcher的 expectWeaklyReachable方法,检查过五秒后activity对象是否有被内存回收。标记内存泄漏。
 * ok
 */
class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {//利用动态代理实现了其他回调方法,感兴趣的可以查看 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)
  }
}

3.8 AndroidOFragmentDestroyWatcher SDK里的fragment监听

todo放流程图

internal class AndroidOFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher //ObjectWatcher类实现了这个接口
) : (Activity) -> Unit {//他是个类,继承了一个(Activity) -> Unit lambda接口,表达式
  //FragmentLifecycleCallbacks接口实例
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null) {
        //监听view泄漏
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      //监听fragment泄漏
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

  //lambda表达式,会调用的地方
  override fun invoke(activity: Activity) {
    val fragmentManager = activity.fragmentManager
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }
}

3.9 FragmentAndViewModelWatcher监听Fragment And ViewModel

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

  private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
    // android框架自带的fragment泄漏监测支持从 AndroidO(26)开始。
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
        AndroidOFragmentDestroyWatcher(reachabilityWatcher)
      )
    }

    getWatcherIfAvailable(
      ANDROIDX_FRAGMENT_CLASS_NAME,
      ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      //ANDROIDX support库里的
      fragmentDestroyWatchers.add(it)
    }

    getWatcherIfAvailable(
      ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
      ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      //ANDROIDX support库里的
      fragmentDestroyWatchers.add(it)
    }
    fragmentDestroyWatchers
  }

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        for (watcher in fragmentDestroyWatchers) {//fragmentDestroyWatchers 函数lambda
          watcher(activity)//调用函数
        }
      }
    }

  override fun install() {
    //注册ActivityLifecycle生命周期监听函数
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

  private fun getWatcherIfAvailable(
    fragmentClassName: String,
    watcherClassName: String,
    reachabilityWatcher: ReachabilityWatcher
  ): ((Activity) -> Unit)? {//? 表示当前对象可以为空,即可以 = null

    return if (classAvailable(fragmentClassName) &&
      classAvailable(watcherClassName)
    ) {
      //如果类存在,则创建监测类
      val watcherConstructor =
        Class.forName(watcherClassName).getDeclaredConstructor(ReachabilityWatcher::class.java)
      @Suppress("UNCHECKED_CAST")
      watcherConstructor.newInstance(reachabilityWatcher) 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
    }
  }

  companion object {
    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,上边的英文写着原因,Jetifier 会迁移指向 android.support.* 软件包的 Java、XML、
            // POM 和 ProGuard 引用,更改它们以使其指向相应的 androidx.* 软件包。
      StringBuilder("android.").append("support.v4.app.Fragment")
        .toString()
    private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
      "leakcanary.internal.AndroidSupportFragmentDestroyWatcher"
  }
}

3.10 RootViewWatcher  Curtains中的监听器会在windows rootView 变化的时候被全局调用。

/**
 * Expects root views to become weakly reachable soon after they are removed from the window
 * manager.
 * Curtains中的监听器会在windows rootView 变化的时候被全局调用。Curtains是squareup 的另一个开源库,
 * Curtains 提供了用于处理 Android 窗口的集中式 API。具体移步他的官方仓库。
 */
class RootViewWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val listener = OnRootViewAddedListener { rootView ->
    //如果是 Dialog TOOLTIP, TOAST, UNKNOWN 等类型的windows
    //trackDetached 为true
    val trackDetached = when(rootView.windowType) {
      PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
          // Activities are already tracked by ActivityWatcher
          is Activity -> false
          is Dialog -> rootView.resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
          // Probably a DreamService
          else -> true
        }
      }
      // Android widgets keep detached popup window instances around.
      POPUP_WINDOW -> false
      TOOLTIP, TOAST, UNKNOWN -> true
    }
    //看到关键代码,就是 在Curtains中添加onRootViewsChangedListeners 监听器。当windowsType类型为 **Dialog(xml文件中配置)**
    // ***TOOLTIP***, ***TOAST***,或者未知的时候 ,在 onViewDetachedFromWindow 的时候监听泄漏情况。
    if (trackDetached) {
      rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {

        val watchDetachedView = Runnable {

          reachabilityWatcher.expectWeaklyReachable(
            rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
          )
        }

        override fun onViewAttachedToWindow(v: View) {
          mainHandler.removeCallbacks(watchDetachedView)
        }

        override fun onViewDetachedFromWindow(v: View) {
          mainHandler.post(watchDetachedView)
        }
      })
    }
  }

  override fun install() {
    Curtains.onRootViewsChangedListeners += listener
  }

  override fun uninstall() {
    Curtains.onRootViewsChangedListeners -= listener
  }
}

3.11 ServiceWatcher监听service泄漏

@SuppressLint("PrivateApi")
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {

  private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()

  private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }

  private val activityThreadInstance by lazy {
    activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!//不为空时执行代码,如果为空,仍会报错;
  }

  //activityThreadServices 是个装了所有<IBinder, Service> 对的Map。代码中可以看到很粗暴地,
  // 直接通过反射从ActivityThread实例中拿到了mServices 变量 。赋值给activityThreadServices。
  private val activityThreadServices by lazy {
    val mServicesField =
      activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }

    @Suppress("UNCHECKED_CAST")
    mServicesField[activityThreadInstance] as Map<IBinder, Service>
  }

  private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
  private var uninstallActivityManager: (() -> Unit)? = null

  override fun install() {
    checkMainThread()
    check(uninstallActivityThreadHandlerCallback == null) {
      "ServiceWatcher already installed"
    }
    check(uninstallActivityManager == null) {
      "ServiceWatcher already installed"
    }
    try {

      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback//mCallback是原来的callback
          }
        }
//        代码中可以看到,主要是对于 STOP_SERVICE 的操作做了一个钩子,在之前执行 onServicePreDestroy。
//        主要作用是为该service 创建一个弱引用,并且加到servicesToBeDestroyed[token] 中 。
        Handler.Callback { msg ->
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            activityThreadServices[key]?.let {
              onServicePreDestroy(key, it)
            }
          }
          mCallback?.handleMessage(msg) ?: false
        }
      }
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        uninstallActivityManager = {
          swapActivityManager { _, _ ->
            activityManagerInstance
          }
        }
//        代码所示,替换后的ActivityManager 在调用serviceDoneExecuting 方法的时候添加了个钩子,
//        如果该service在之前加入的servicesToBeDestroyed map中,则调用onServiceDestroyed 监测该service内存泄漏。
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              onServiceDestroyed(token)
            }
          }
          try {
            if (args == null) {
              method.invoke(activityManagerInstance)
            } else {
              method.invoke(activityManagerInstance, *args)
            }
          } catch (invocationException: InvocationTargetException) {
            throw invocationException.targetException
          }
        }
      }
    } catch (ignored: Throwable) {
      SharkLog.d(ignored) { "Could not watch destroyed services" }
    }
  }

  override fun uninstall() {
    checkMainThread()
    uninstallActivityManager?.invoke()
    uninstallActivityThreadHandlerCallback?.invoke()
    uninstallActivityManager = null
    uninstallActivityThreadHandlerCallback = null
  }

  //主要作用是为该service 创建一个弱引用,并且加到servicesToBeDestroyed[token] 中
  private fun onServicePreDestroy(
    token: IBinder,
    service: Service
  ) {
    servicesToBeDestroyed[token] = WeakReference(service)
  }

  private fun onServiceDestroyed(token: IBinder) {
    servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
      serviceWeakReference.get()?.let { service ->
        reachabilityWatcher.expectWeaklyReachable(
          service, "${service::class.java.name} received Service#onDestroy() callback"
        )
      }
    }
  }

  //拿到ActivityThread 的Handler,将其回调的 handleMessage,换成加了料的Handler.Callback,加料代码如下
  //swap传入一个返回一个,所以可以swap
  private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
    val mHField =
      activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
    val mH = mHField[activityThreadInstance] as Handler

    val mCallbackField =
      Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
    val mCallback = mCallbackField[mH] as Handler.Callback?
    mCallbackField[mH] = swap(mCallback)//传入的是要换的
  }

  //该方法完成了将ActivityManager替换成IActivityManager的一个动态代理类。代码如下
  @SuppressLint("PrivateApi")
  private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
    val singletonClass = Class.forName("android.util.Singleton")
    val mInstanceField =
      singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }

    val singletonGetMethod = singletonClass.getDeclaredMethod("get")

    val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      "android.app.ActivityManager" to "IActivityManagerSingleton"
    } else {
      "android.app.ActivityManagerNative" to "gDefault"
    }

    val activityManagerClass = Class.forName(className)
    val activityManagerSingletonField =
      activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
    val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]

    // Calling get() instead of reading from the field directly to ensure the singleton is
    // created.
    val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)

    val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
    mInstanceField[activityManagerSingletonInstance] =
      swap(iActivityManagerInterface, activityManagerInstance!!)
  }

  companion object {//伴生对象,静态内部类,里边的都是静态值
    private const val STOP_SERVICE = 116

    private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
  }
}

4.leakcanary-object-watcher-android-androidx

4.1 AndroidXFragmentDestroyWatcher

/**
 * 这个类继承自一个函数,invoke是函数的方法,相当于一个接口!
 * ok
 */
internal class AndroidXFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {

  //FragmentLifecycleCallbacks Fragment生命周期监听
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    //create的时候
    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      //ViewModelClearedWatcher install fragment
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null) {
        //expectWeaklyReachable 对view进行监听
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      //expectWeaklyReachable 对fragment进行监听
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

  override fun invoke(activity: Activity) {//invoke是kotlin的高阶函数,在调用的时候的方法
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      //注册fragmentLifecycleCallbacks
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      //ViewModelClearedWatcher install activity
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
}

4.2 ViewModelClearedWatcher 往ViewModelStoreOwner即Activity,Fragment,里添加一个监控ViewModel,监控onCleared方法

/**
 * spy 间谍
 * [AndroidXFragmentDestroyWatcher] calls [install] to add a spy [ViewModel] in every
 * [ViewModelStoreOwner] instance (i.e. FragmentActivity and Fragment). [ViewModelClearedWatcher]
 * holds on to the map of [ViewModel]s backing its store. When [ViewModelClearedWatcher] receives
 * the [onCleared] callback, it adds each live [ViewModel] from the store to the [ReachabilityWatcher].
 *
 * 什么情况下viewmodel会泄漏?
 *
 */
//该watcher 继承了ViewModel,生命周期被 ViewModelStoreOwner 管理。
//添加一个间谍ViewModel,用于监听onCleared事件
internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {

  private val viewModelMap: Map<String, ViewModel>?

  init {
    // We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,
    // however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0
    // does not have ViewModelStore#keys. All versions currently have the mMap field.
    //通过反射获取所有的 store 存储的所有viewModelMap
    viewModelMap = try {
      val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
      mMapField.isAccessible = true
      @Suppress("UNCHECKED_CAST")
      mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
    } catch (ignored: Exception) {
      null
    }
  }

  override fun onCleared() {
    // viewmodle 被清理释放的时候回调,检查所有viewmodle 是否会有泄漏
    viewModelMap?.values?.forEach { viewModel ->
      reachabilityWatcher.expectWeaklyReachable(
        viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
      )
    }
  }

  companion object {
    fun install(//伴生函数,相当于静态方法
      storeOwner: ViewModelStoreOwner,
      reachabilityWatcher: ReachabilityWatcher
    ) {
      val provider = ViewModelProvider(storeOwner, object : Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T =
          ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
      })
      //获取ViewModelClearedWatcher实例
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
}

5.leakcanary-object-watcher-android-support-fragments

5.1 AndroidSupportFragmentDestroyWatcher

internal class AndroidSupportFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {

  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null) {
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

  override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
    }
  }
}

6.shark-log

6.1 SharkLog.kt

object SharkLog {

  /**
   * @see SharkLog
   */
  interface Logger {

    /**
     * Logs a debug message formatted with the passed in arguments.
     */
    fun d(message: String)

    /**
     * Logs a [Throwable] and debug message formatted with the passed in arguments.
     */
    fun d(
      throwable: Throwable,
      message: String
    )
  }

  @Volatile var logger: Logger? = null

  /**
   * @see Logger.d
   */
  inline fun d(message: () -> String) {
    // Local variable to prevent the ref from becoming null after the null check.
    val logger = logger ?: return//如果不为空返回logger,如果为空则返回
    logger.d(message.invoke())
  }

  /**
   * @see Logger.d
   */
  inline fun d(
    throwable: Throwable,
    message: () -> String
  ) {
    // Local variable to prevent the ref from becoming null after the null check.
    val logger = logger ?: return
    logger.d(throwable, message.invoke())
  }
}

7.plumber-android

8.leakcanary-android-core

8.1 Friendly.kt 主线程相关的

//ok
package leakcanary.internal.friendly

//主线程handler
internal inline val mainHandler
  get() = leakcanary.internal.mainHandler

//是否主线程
internal inline fun checkMainThread() = leakcanary.internal.checkMainThread()

internal inline fun checkNotMainThread() = leakcanary.internal.checkNotMainThread()

//动态代理
internal inline fun <reified T : Any> noOpDelegate(): T = leakcanary.internal.noOpDelegate()

//计算某个方法时间
internal inline fun measureDurationMillis(block: () -> Unit) =
  leakcanary.internal.measureDurationMillis(block)

8.2 Size.kit 返回大小

// https://stackoverflow.com/a/3758880
//ok
//1KB = 1000Byte
//1Kib = 1024Byte
internal fun humanReadableByteCount(
        bytes: Long,
        si: Boolean//是否是十进制千单位
): String {
    val unit = if (si) 1000 else 1024
    if (bytes < unit) return "$bytes B"
    //ln自然对数,以常数e为底数的对数。记作lnN(N>0)。在物理学,生物学等自然科学中有重要的意义。
    // 一般表示方法为lnx。数学中也常见以logx表示自然对数。若为了避免与基为10的常用对数lgx混淆,可用“全写”㏒ex。
    val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
    //K 与 Ki 分别表示 kilo-(千) 与 kibi-(二进制千) 。作为前缀使用时, k 表示 1,000,Ki 表示1,024。
    //“Ki”来源于它在计算机方面 210 = 1,024 的使用。换算:1KiB = 1024 B,1MiB = 1024 KiB,1GiB = 1024 MiB。
    val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
    return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
}

8.3 Tuples.kt   Pair变为Triple

//Kotlin允许在不使用括号和点号的情况下调用函数,那么这种函数被称为 infix函数。
internal infix fun <A, B, C> Pair<A, B>.to(that: C): Triple<A, B, C> = Triple(first, second, that)

8.4 OnRetainInstanceListener.kt 监听RetainInstanceEvent4种情况

internal sealed class RetainInstanceEvent {//密封类
    object NoMoreObjects : RetainInstanceEvent()
    sealed class CountChanged : RetainInstanceEvent() {
        class BelowThreshold(val retainedCount: Int) : RetainInstanceEvent()
        class DumpingDisabled(val reason: String) : RetainInstanceEvent()
        object DumpHappenedRecently : RetainInstanceEvent()
    }
}

/**
 * Called by LeakCanary when the number of retained instances updates .
 */
internal interface OnRetainInstanceListener {

    /**
     * Called when there's a change to the Retained Instances. See [RetainInstanceEvent] for
     * possible events.
     */
    fun onEvent(event: RetainInstanceEvent)
}

internal class DefaultOnRetainInstanceListener : OnRetainInstanceListener {

    override fun onEvent(event: RetainInstanceEvent) {}
}

8.5 TvOnRetainInstanceListener tv相关通知

//tv相关通知
internal class TvOnRetainInstanceListener(private val application: Application) :
  OnRetainInstanceListener {

  override fun onEvent(event: RetainInstanceEvent) {
    val message = when (event) {
      NoMoreObjects -> {
        //All retained objects were garbage collected
        application.getString(R.string.leak_canary_notification_no_retained_object_title)
      }
      is BelowThreshold -> {
        //%1$d retained objects. Heap dump threshold is %2$d.\nBackground the app to trigger heap dump immediately
        //tv是把程序放到后台的时候,heapdump ??? todo
        application.getString(
          R.string.leak_canary_tv_toast_retained_objects,
          event.retainedCount,
          LeakCanary.config.retainedVisibleThreshold
        )
      }
      is DumpingDisabled -> {
        event.reason
      }
      //一分钟前dump了
      is DumpHappenedRecently -> {
        application.getString(R.string.leak_canary_notification_retained_dump_wait)
      }
    }
    SharkLog.d { message }

    mainHandler.post {
      //显示toast,得到resumed activity
      val resumedActivity = InternalLeakCanary.resumedActivity ?: return@post
      TvToast.makeText(resumedActivity, message).show()
    }
  }
}

8.6 TvToast 电视上 toast

//tv相关通知
internal object TvToast {

  /**
   * Make an Android TV toast.
   * Don't forget to call [Toast.show] to display the toast!
   * @param activity Currently resumed [Activity] to display toast on. Note that it's not [Context]
   *        to prevent passing application context that could lead to crashes on older platforms.
   * @param text The text to show. Can be formatted text.
   */
  @SuppressLint("ShowToast")
  fun makeText(
    activity: Activity,//application的可能crash
    text: CharSequence
  ): Toast {
    val toast: Toast = Toast.makeText(activity, text, Toast.LENGTH_LONG)
    val textView = toast.view.findViewById<TextView>(android.R.id.message)
    textView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.leak_canary_icon, 0, 0, 0)
    textView.compoundDrawablePadding =
      activity.resources.getDimensionPixelSize(R.dimen.leak_canary_toast_icon_tv_padding)
    return toast
  }
}

8.7 OnHeapAnalyzedListener 分析之后调用接口

//分析之后调用接口
fun interface OnHeapAnalyzedListener {//fun interface 只有一个方法的接口

  /**
   * @see OnHeapAnalyzedListener
   */
  //HeapAnalysis shark模块里的
  fun onHeapAnalyzed(heapAnalysis: HeapAnalysis)

  //伴生对象,里边的方法都是静态的
  companion object {
    /**
     * Utility function to create a [OnHeapAnalyzedListener] from the passed in [block] lambda
     * instead of using the anonymous `object : OnHeapAnalyzedListener` syntax.
     *
     * Usage:
     *
     * ```kotlin
     * val listener = OnHeapAnalyzedListener {
     *
     * }
     * ```
     */
    //内联函数
    //operator 类的实例可以直接用()调用的函数
    //crossinline 通畅,穿越inline函数block里不能有return
    //invoke 方法,Lambda 表达式调用位置 {}
    //
    inline operator fun invoke(crossinline block: (HeapAnalysis) -> Unit): OnHeapAnalyzedListener =
      object : OnHeapAnalyzedListener {
        override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) {
          block(heapAnalysis)
        }
      }
  }
}

8.8 Serializables.kt序列化,反序列化

//将可序列化的对象序列化为ByteArray
internal fun Serializable.toByteArray(): ByteArray {
    val outputStream = ByteArrayOutputStream()
    ObjectOutputStream(outputStream).writeObject(this)
    return outputStream.toByteArray()
}

internal object Serializables {
    //reified 具体化
    //在kotlin中一个内联函数(inline)可以被具体化(reified),这意味着我们可以得到使用泛型类型的Class。
    //https://blog.csdn.net/u011215710/article/details/103906478
    //泛型在运行时会被类型擦除,但是在inline函数中我们可以指定类型不被擦除, 因为inline函数在编译期会将字节码copy到调用它的方法里,
    //所以编译器会知道当前的方法中泛型对应的具体类型是什么,然后把泛型替换为具体类型,从而达到不被擦除的目的,
    // 在inline函数中我们可以通过reified关键字来标记这个泛型在编译时替换成具体类型
    inline fun <reified T> fromByteArray(byteArray: ByteArray): T? {
        val inputStream = ByteArrayInputStream(byteArray)
        return try {
//            https://blog.csdn.net/sinat_21693123/article/details/80876623
            //as?  as运算符 尝试把值转换成指定的类型,如果值不是合适的类型就返回null
            ObjectInputStream(inputStream).readObject() as? T
        } catch (ignored: Throwable) {
            SharkLog.d(ignored) { "Could not deserialize bytes, ignoring" }
            null
        }
    }
}

8.9 DefaultOnHeapAnalyzedListener 默认的OnHeapAnalyzedListener实现类

/**
 * 默认的OnHeapAnalyzedListener实现类
 */
class DefaultOnHeapAnalyzedListener private constructor(private val applicationProvider: () -> Application) :
        OnHeapAnalyzedListener {

    // Kept this constructor for backward compatibility of public API.
    @Deprecated(
            message = "Use DefaultOnHeapAnalyzedListener.create() instead",
            replaceWith = ReplaceWith("DefaultOnHeapAnalyzedListener.create()")
    )
    constructor(application: Application) : this({ application })

    //获取application
    private val application: Application by lazy { applicationProvider() }
    
    override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) {
        SharkLog.d { "\u200B\n${LeakTraceWrapper.wrap(heapAnalysis.toString(), 120)}" }

        //插入数据库
        val db = LeaksDbHelper(application).writableDatabase
        val id = HeapAnalysisTable.insert(db, heapAnalysis)
        db.releaseReference()

        //根据结果确定通知点击之后显示的screen,Pair对
        val (contentTitle, screenToShow) = when (heapAnalysis) {
            //失败去失败页
            is HeapAnalysisFailure -> application.getString(
                    R.string.leak_canary_analysis_failed
            ) to HeapAnalysisFailureScreen(id)
            //成功去成功
            is HeapAnalysisSuccess -> {
                val retainedObjectCount = heapAnalysis.allLeaks.sumBy { it.leakTraces.size }
                val leakTypeCount = heapAnalysis.applicationLeaks.size + heapAnalysis.libraryLeaks.size
                application.getString(
                        //Found %1$d retained objects grouped as %2$d distinct leaks
                        R.string.leak_canary_analysis_success_notification, retainedObjectCount, leakTypeCount
                ) to HeapDumpScreen(id)
            }
        }

        if (InternalLeakCanary.formFactor == TV) {
            showToast(heapAnalysis)
            printIntentInfo()
        } else {
            //发送通知
            showNotification(screenToShow, contentTitle)
        }
    }

    private fun showNotification(
            screenToShow: Screen,
            contentTitle: String
    ) {
        //通知跳转页面,传入两个页面,基础页面是HeapDumpsScreen,然后根据成功失败来显示screenToShow
        val pendingIntent = LeakActivity.createPendingIntent(
                application, arrayListOf(HeapDumpsScreen(), screenToShow)
        )

        val contentText = application.getString(R.string.leak_canary_notification_message)
        //显示通知
        Notifications.showNotification(
                application, contentTitle, contentText, pendingIntent,
                R.id.leak_canary_notification_analysis_result,
                LEAKCANARY_MAX
        )
    }

    /**
     * Android TV devices do not have notifications, therefore the only easy and non-invasive way
     * to communicate with user is via Toast messages. These are used just to grab user attention and
     * to direct them to Logcat where a much more detailed report will be printed.
     */
    private fun showToast(heapAnalysis: HeapAnalysis) {
        mainHandler.post {
            val resumedActivity = InternalLeakCanary.resumedActivity ?: return@post
            val message: String = when (heapAnalysis) {
                is HeapAnalysisSuccess -> {
                    application.getString(
                            R.string.leak_canary_tv_analysis_success,
                            heapAnalysis.applicationLeaks.size,
                            heapAnalysis.libraryLeaks.size
                    )
                }
                is HeapAnalysisFailure -> application.getString(R.string.leak_canary_tv_analysis_failure)
            }
            TvToast.makeText(resumedActivity, message)
                    .show()
        }
    }

    /**
     * Android TV with API 26+ has a bug where the launcher icon doesn't appear, so users won't know how
     * to launch LeakCanary Activity.
     * This method prints an adb command that launched LeakCanary into the logcat
     */
    private fun printIntentInfo() {
        val leakClass = LeakActivity::class.java
        SharkLog.d {
            """====================================
  ANDROID TV LAUNCH INTENT
  ====================================
  Run the following adb command to display the list of leaks:
  
  adb shell am start -n "${application.packageName}/${leakClass.`package`?.name}.LeakLauncherActivity"
  ===================================="""
        }
    }

    companion object {
        //伴生对象,单例
        fun create(): OnHeapAnalyzedListener =
                DefaultOnHeapAnalyzedListener { InternalLeakCanary.application }
    }
}

8.10 NotificationType 通知类型,包括通知channel,重要性importance

//通知类型,包括通知channel,重要性importance
internal enum class NotificationType(
        val nameResId: Int,
        val importance: Int
) {
    LEAKCANARY_LOW(
            //LeakCanary Low Priority
            R.string.leak_canary_notification_channel_low, IMPORTANCE_LOW
    ),
    LEAKCANARY_MAX(
            //LeakCanary Result
            R.string.leak_canary_notification_channel_result, IMPORTANCE_MAX
    );
}

private const val IMPORTANCE_LOW = 2
private const val IMPORTANCE_MAX = 5

8.11 DebuggerControl isDebuggerConnected连着的时候会有问题,例如创建临时对象,debugger连着的时候,leakcanary不支持

internal object DebuggerControl {

  val isDebuggerAttached: Boolean
    get() = Debug.isDebuggerConnected()
}

8.12 RequestStoragePermissionActivity权限申请

@TargetApi(Build.VERSION_CODES.M) //
internal class RequestStoragePermissionActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (savedInstanceState == null) {
            if (hasStoragePermission()) {
                finish()
                return
            }
            val permissions = arrayOf(WRITE_EXTERNAL_STORAGE)
            requestPermissions(permissions, 42)
        }
    }

    override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<String>,
            grantResults: IntArray
    ) {
        if (!hasStoragePermission()) {
            Toast.makeText(application, R.string.leak_canary_permission_not_granted, LENGTH_LONG)
                    .show()
        }
        finish()
    }

    override fun finish() {
        // Reset the animation to avoid flickering.
        overridePendingTransition(0, 0)
        super.finish()
    }

    private fun hasStoragePermission(): Boolean {
        return checkSelfPermission(WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED
    }

    companion object {

        fun createPendingIntent(context: Context): PendingIntent {
            val intent = Intent(context, RequestStoragePermissionActivity::class.java)
            intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TOP
            //https://blog.csdn.net/bdmh/article/details/41804695
            //https://blog.csdn.net/sinat_31057219/article/details/88743458?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163107200416780274110200%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163107200416780274110200&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-88743458.first_rank_v2_pc_rank_v29&utm_term=FLAG_IMMUTABLE&spm=1018.2226.3001.4187
            //FLAG_UPDATE_CURRENT
            //如果要创建的PendingIntent已经存在了,那么在保留原先PendingIntent的同时,
            //将原先PendingIntent封装的Intent中的extra部分替换为现在新创建的PendingIntent的intent中extra的内容
            val flags = if (Build.VERSION.SDK_INT >= 23) {
                FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE// FLAG_IMMUTABLE 设置 Intent 在 send 的时候不能更改
            } else {
                FLAG_UPDATE_CURRENT//更新
            }
            return PendingIntent.getActivity(context, 1, intent, flags)
        }
    }
}

8.13 VisibilityTracker 亮屏,并且有可见的activity

internal class VisibilityTracker(
        private val listener: (Boolean) -> Unit
) : Application.ActivityLifecycleCallbacks by noOpDelegate(), BroadcastReceiver() {

    private var startedActivityCount = 0

    /**
     * Visible activities are any activity started but not stopped yet. An activity can be paused
     * yet visible: this will happen when another activity shows on top with a transparent background
     * and the activity behind won't get touch inputs but still need to render / animate.
     */
    private var hasVisibleActivities: Boolean = false

    /**
     * Assuming screen on by default.
     */
    private var screenOn: Boolean = true

    private var lastUpdate: Boolean = false

    override fun onActivityStarted(activity: Activity) {
        startedActivityCount++
        if (!hasVisibleActivities && startedActivityCount == 1) {
            hasVisibleActivities = true
            updateVisible()
        }
    }

    override fun onActivityStopped(activity: Activity) {
        // This could happen if the callbacks were registered after some activities were already
        // started. In that case we effectively considers those past activities as not visible.
        if (startedActivityCount > 0) {
            startedActivityCount--
        }
        if (hasVisibleActivities && startedActivityCount == 0 && !activity.isChangingConfigurations) {
            hasVisibleActivities = false
            updateVisible()
        }
    }

    override fun onReceive(
            context: Context,
            intent: Intent
    ) {
        screenOn = intent.action != ACTION_SCREEN_OFF
        updateVisible()
    }

    private fun updateVisible() {
        val visible = screenOn && hasVisibleActivities//亮屏,并且有可见的activity
        if (visible != lastUpdate) {
            lastUpdate = visible
            listener.invoke(visible)//lambda表达式调用
        }
    }
}

//注册亮屏,熄屏监听
internal fun Application.registerVisibilityListener(listener: (Boolean) -> Unit) {
    val visibilityTracker = VisibilityTracker(listener)
    registerActivityLifecycleCallbacks(visibilityTracker)
    registerReceiver(visibilityTracker, IntentFilter().apply {
        addAction(ACTION_SCREEN_ON)
        addAction(ACTION_SCREEN_OFF)
    })
}

8.14  HeapDumper返回DumpHeapResult,有两种HeapDump NoHeapDump

/** Dumps the heap into a file.  */
internal interface HeapDumper {

  /**
   * @return a [File] referencing the dumped heap, or [.RETRY_LATER] if the heap could
   * not be dumped.
   *
   * 如果dump成功 返回一个dumped heap
   * 如果dump不成功返回.RETRY_LATER,是啥?todo
   */
  fun dumpHeap(): DumpHeapResult
}

/** Dump heap result holding the file and the dump heap duration */
internal sealed class DumpHeapResult//密封类

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

internal object NoHeapDump : DumpHeapResult()

8.15 NotificationReceiver通知

internal class NotificationReceiver : BroadcastReceiver() {
    //两种情况
    enum class Action {
        DUMP_HEAP,
        CANCEL_NOTIFICATION
    }

    override fun onReceive(
            context: Context,
            intent: Intent
    ) {
        when (intent.action) {
            DUMP_HEAP.name -> {
                //onDumpHeapReceived 去dump
                InternalLeakCanary.onDumpHeapReceived(forceDump = false)
            }
            CANCEL_NOTIFICATION.name -> {
                // Do nothing, the notification has auto cancel true.
            }
            else -> {
                SharkLog.d { "NotificationReceiver received unknown intent action for $intent" }
            }
        }
    }

    companion object {
        fun pendingIntent(
                context: Context,
                action: Action//上边定义的enum
        ): PendingIntent {
            val broadcastIntent = Intent(context, NotificationReceiver::class.java)
            broadcastIntent.action = action.name//上边定义的enum 的name
            val flags = if (Build.VERSION.SDK_INT >= 23) {
                PendingIntent.FLAG_IMMUTABLE//不变的,具体用途 todo
            } else {
                0
            }
            return PendingIntent.getBroadcast(context, 0, broadcastIntent, flags)//注意这是Broadcast
        }
    }
}

8.16 LeakCanarySingleThreadFactory 没用到

/**
 * This is intended to only be used with a single thread executor.
 * 只用于单线程线程池
 * nouse
 */
internal class LeakCanarySingleThreadFactory(threadName: String) : ThreadFactory {

  private val threadName: String = "LeakCanary-$threadName"

  override fun newThread(runnable: Runnable): Thread {
    return Thread(runnable, threadName)
  }
}

8.17 AndroidHeapDumper 用于dump,并计算dump的时间

//用于dump,并计算dump的时间
internal class AndroidHeapDumper(
        context: Context,
        private val leakDirectoryProvider: LeakDirectoryProvider//文件位置
) : HeapDumper {

    private val context: Context = context.applicationContext

    override fun dumpHeap(): DumpHeapResult {
        //创建新的hprof 文件
        val heapDumpFile = leakDirectoryProvider.newHeapDumpFile() ?: return NoHeapDump

        val waitingForToast = FutureResult<Toast?>()

        //展示dump吐司
        showToast(waitingForToast)
        //如果展示吐司时间超过五秒,就不dump了
        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) {
            //Dumping Heap
            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)
        }
        //获取toast,一会用去取消
        val toast = waitingForToast.get()

        return try {
            val durationMillis = measureDurationMillis {
                //调用DumpHprofData
                Debug.dumpHprofData(heapDumpFile.absolutePath)
            }
            if (heapDumpFile.length() == 0L) {
                SharkLog.d { "Dumped heap file is 0 byte length" }
                NoHeapDump
            } else {
                HeapDump(file = heapDumpFile, durationMillis = durationMillis)
            }
        } catch (e: Exception) {
            SharkLog.d(e) { "Could not dump heap" }
            // Abort heap dump
            NoHeapDump
        } finally {
            //取消toast
            cancelToast(toast)
            notificationManager.cancel(R.id.leak_canary_notification_dumping_heap)
        }
    }

    private fun showToast(waitingForToast: FutureResult<Toast?>) {
        mainHandler.post(Runnable {
            val resumedActivity = InternalLeakCanary.resumedActivity
            if (resumedActivity == null) {
                waitingForToast.set(null)
                return@Runnable//@代表返回位置
            }
            val toast = Toast(resumedActivity)
            // Resources from application context: https://github.com/square/leakcanary/issues/2023
            val iconSize = context.resources.getDimensionPixelSize(
                    R.dimen.leak_canary_toast_icon_size
            )
            toast.setGravity(Gravity.CENTER_VERTICAL, 0, -iconSize)
            toast.duration = Toast.LENGTH_LONG
            // Inflating with application context: https://github.com/square/leakcanary/issues/1385
            val inflater = LayoutInflater.from(context)
            toast.view = inflater.inflate(R.layout.leak_canary_heap_dump_toast, null)
            toast.show()

            val toastIcon = toast.view.findViewById<View>(R.id.leak_canary_toast_icon)
            toastIcon.translationY = -iconSize.toFloat()
            toastIcon
                    .animate()
                    .translationY(0f)
                    .setListener(object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            waitingForToast.set(toast)
                        }
                    })
        })
    }

    //在主线程cancel toast
    private fun cancelToast(toast: Toast?) {
        if (toast == null) {
            return
        }
        mainHandler.post { toast.cancel() }
    }
}

8.18 ForegroundService 创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作

//https://blog.csdn.net/javazejian/article/details/52426425
//IntentService的特点:
//它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类
//它可以用于在后台执行耗时的异步任务,当任务完成后会自动停止
//它拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务
//它内部通过HandlerThread和Handler实现异步操作
//创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作
internal abstract class ForegroundService(
        name: String,
        private val notificationContentTitleResId: Int,
        private val notificationId: Int
) : IntentService(name) {

    override fun onCreate() {
        super.onCreate()
        showForegroundNotification(
                max = 100, progress = 0, indeterminate = true,//模糊的; 不确定的; 难以识别的;
                contentText = getString(R.string.leak_canary_notification_foreground_text)//LeakCanary is working.
        )
    }

    protected fun showForegroundNotification(
            max: Int,
            progress: Int,
            indeterminate: Boolean,
            contentText: String
    ) {
        val builder = Notification.Builder(this)
                .setContentTitle(getString(notificationContentTitleResId))
                .setContentText(contentText)//LeakCanary is working.
                .setProgress(max, progress, indeterminate)
        val notification =
                Notifications.buildNotification(this, builder, LEAKCANARY_LOW)
        startForeground(notificationId, notification)
    }

  /**
   * 实现异步任务的方法
   * @param intent Activity传递过来的Intent,数据封装在intent中
   */
    override fun onHandleIntent(intent: Intent?) {
        onHandleIntentInForeground(intent)
    }

    protected abstract fun onHandleIntentInForeground(intent: Intent?)

    override fun onDestroy() {
        super.onDestroy()
        stopForeground(true)
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }
}

8.19 FutureResult使用门拴控制 latch到了0,返回true,时间到了还没为0,返回false

internal class FutureResult<T> {
  //    AtomicReference类提供了一个可以原子读写的对象引用变量。
//    原子意味着尝试更改相同AtomicReference的多个线程(例如,使用比较和交换操作)不会使AtomicReference最终达到不一致的状态。
    private val resultHolder: AtomicReference<T> = AtomicReference()
  //    countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
//    是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,
//    计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
    private val latch: CountDownLatch = CountDownLatch(1)//latch门闩; 插销; 碰锁; 弹簧锁;

  fun wait(
            timeout: Long,
            unit: TimeUnit
    ): Boolean {
        try {
          //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
          //await(timeout,...)和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
            //latch到了0,返回true,时间到了还没为0,返回false
            return latch.await(timeout, unit)
        } catch (e: InterruptedException) {
            throw RuntimeException("Did not expect thread to be interrupted", e)
        }
    }

    fun get(): T {
        if (latch.count > 0) {
            throw IllegalStateException("Call wait() and check its result")
        }
        return resultHolder.get()
    }

    fun set(result: T) {
        resultHolder.set(result)
        //将count值减1
        latch.countDown()
    }
}

8.20 LeakDirectoryProvider dump文件位置

internal class LeakDirectoryProvider constructor(
        context: Context,
        private val maxStoredHeapDumps: () -> Int,//7
        private val requestExternalStoragePermission: () -> Boolean//false
) {
    private val context: Context = context.applicationContext

    fun listFiles(filter: FilenameFilter): MutableList<File> {
        if (!hasStoragePermission() && requestExternalStoragePermission()) {
            requestWritePermissionNotification()
        }
        val files = ArrayList<File>()

        val externalFiles = externalStorageDirectory().listFiles(filter)//externalStorageDirectory
        if (externalFiles != null) {
            files.addAll(externalFiles)
        }

        val appFiles = appStorageDirectory().listFiles(filter)//appStorageDirectory
        if (appFiles != null) {
            files.addAll(appFiles)
        }
        return files
    }

    fun newHeapDumpFile(): File? {
        cleanupOldHeapDumps()

        var storageDirectory = externalStorageDirectory()//先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()//转为appStorageDirectory
            if (!directoryWritableAfterMkdirs(storageDirectory)) {
                SharkLog.d {
                    "Could not create heap dump directory in app storage: [${storageDirectory.absolutePath}]"
                }
                return null
            }
        }

        val fileName = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS'.hprof'", Locale.US).format(Date())
        return File(storageDirectory, fileName)
    }

    @TargetApi(M)//是否有权限
    fun hasStoragePermission(): Boolean {
        if (SDK_INT < M) {
            return true
        }
        // Once true, this won't change for the life of the process so we can cache it.
        if (writeExternalStorageGranted) {
            return true
        }
        writeExternalStorageGranted =
                context.checkSelfPermission(WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED
        return writeExternalStorageGranted
    }

    //发通知要权限,通知咋没看见过,因为默认是不开的
    fun requestWritePermissionNotification() {
        if (permissionNotificationDisplayed) {
            return
        }
        permissionNotificationDisplayed = true

        val pendingIntent = RequestStoragePermissionActivity.createPendingIntent(context)
        val contentTitle = context.getString(
                R.string.leak_canary_permission_notification_title
        )
        val packageName = context.packageName
        val contentText =
                //Click to enable storage permission for %s.
                context.getString(R.string.leak_canary_permission_notification_text, packageName)

        Notifications.showNotification(
                context, contentTitle, contentText, pendingIntent,
                R.id.leak_canary_notification_write_permission, LEAKCANARY_LOW
        )
    }

    //Download下的leakcanary-开头的
    @Suppress("DEPRECATION")
    private fun externalStorageDirectory(): File {
        val downloadsDirectory = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)
        return File(downloadsDirectory, "leakcanary-" + context.packageName)
    }

    //app目录
    private fun appStorageDirectory(): File {
        val appFilesDirectory = context.cacheDir
        return File(appFilesDirectory, "leakcanary")
    }

    private fun directoryWritableAfterMkdirs(directory: File): Boolean {
        val success = directory.mkdirs()
        return (success || directory.exists()) && directory.canWrite()
    }

    private fun cleanupOldHeapDumps() {
        val hprofFiles = listFiles(FilenameFilter { _, name ->
            name.endsWith(
                    HPROF_SUFFIX
            )
        })
        val maxStoredHeapDumps = maxStoredHeapDumps()//最大为7
        if (maxStoredHeapDumps < 1) {
            throw IllegalArgumentException("maxStoredHeapDumps must be at least 1")
        }

        val filesToRemove = hprofFiles.size - maxStoredHeapDumps
        if (filesToRemove > 0) {
            SharkLog.d { "Removing $filesToRemove heap dumps" }
            // Sort with oldest modified first.
            hprofFiles.sortWith(Comparator { lhs, rhs ->
                java.lang.Long.valueOf(lhs.lastModified())
                        .compareTo(rhs.lastModified())
            })
            for (i in 0 until filesToRemove) {
                val path = hprofFiles[i].absolutePath
                val deleted = hprofFiles[i].delete()//具体的删除的地方
                if (deleted) {
                    filesDeletedTooOld += path//加入到list里
                } else {
                    SharkLog.d { "Could not delete old hprof file ${hprofFiles[i].path}" }
                }
            }
        }
    }

    companion object {
        @Volatile
        private var writeExternalStorageGranted: Boolean = false

        @Volatile
        private var permissionNotificationDisplayed: Boolean = false

        private val filesDeletedTooOld = mutableListOf<String>()
        val filesDeletedRemoveLeak = mutableListOf<String>()

        private const val HPROF_SUFFIX = ".hprof"

        fun hprofDeleteReason(file: File): String {
            val path = file.absolutePath
            return when {
                filesDeletedTooOld.contains(path) -> "older than all other hprof files"//因为older被删
                filesDeletedRemoveLeak.contains(path) -> "leak manually removed"//手动删除
                else -> "unknown"
            }
        }
    }
}

8.21 HeapAnalyzerService heap分析服务,后台线程里分析

/**
 * This service runs in a main app process.
 * heap分析服务
 * ForegroundService需要有个通知吧
 */
internal class HeapAnalyzerService : ForegroundService(
        HeapAnalyzerService::class.java.simpleName,
        R.string.leak_canary_notification_analysing,
        R.id.leak_canary_notification_analyzing_heap
), OnAnalysisProgressListener {

    override fun onHandleIntentInForeground(intent: Intent?) {
        if (intent == null || !intent.hasExtra(HEAPDUMP_FILE_EXTRA)) {
            SharkLog.d { "HeapAnalyzerService received a null or empty intent, ignoring." }
            return
        }

        // Since we're running in the main process we should be careful not to impact it.
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
        val heapDumpFile = intent.getSerializableExtra(HEAPDUMP_FILE_EXTRA) as File     //文件
        val heapDumpReason = intent.getStringExtra(HEAPDUMP_REASON_EXTRA)               //原因
        val heapDumpDurationMillis = intent.getLongExtra(HEAPDUMP_DURATION_MILLIS_EXTRA, -1)//dump时间

        val config = LeakCanary.config
        val heapAnalysis = if (heapDumpFile.exists()) {
            analyzeHeap(heapDumpFile, config)
        } else {
            missingFileFailure(heapDumpFile)
        }
        val fullHeapAnalysis = when (heapAnalysis) {
            //分析成功
            is HeapAnalysisSuccess -> heapAnalysis.copy(//一般copy用来原数据不被改变
                    dumpDurationMillis = heapDumpDurationMillis,//设置dumpDurationMillis
                    metadata = heapAnalysis.metadata + ("Heap dump reason" to heapDumpReason)//map加上一个
            )
            //分析失败
            is HeapAnalysisFailure -> heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
        }
        //省略完善分析结果属性的代码
        onAnalysisProgress(REPORTING_HEAP_ANALYSIS)//分析完成
        config.onHeapAnalyzedListener.onHeapAnalyzed(fullHeapAnalysis)
    }

    private fun analyzeHeap(
            heapDumpFile: File,
            config: Config
    ): HeapAnalysis {
        //分析用HeapAnalyzer
        val heapAnalyzer = HeapAnalyzer(this)//回调他自己onAnalysisProgress

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

    private fun missingFileFailure(
            heapDumpFile: File
    ): HeapAnalysisFailure {
        val deletedReason = LeakDirectoryProvider.hprofDeleteReason(heapDumpFile)
        val exception = IllegalStateException(
                "Hprof file $heapDumpFile missing, deleted because: $deletedReason"
        )
        return HeapAnalysisFailure(
                heapDumpFile = heapDumpFile,
                createdAtTimeMillis = System.currentTimeMillis(),
                analysisDurationMillis = 0,
                exception = HeapAnalysisException(exception)
        )
    }

    //onAnalysisProgress总有10步
    override fun onAnalysisProgress(step: OnAnalysisProgressListener.Step) {
        val percent =
                (100f * step.ordinal / OnAnalysisProgressListener.Step.values().size).toInt()
        SharkLog.d { "Analysis in progress, working on: ${step.name}" }
        val lowercase = step.name.replace("_", " ")
                .toLowerCase(Locale.US)
        val message = lowercase.substring(0, 1).toUpperCase(Locale.US) + lowercase.substring(1)
        showForegroundNotification(100, percent, false, message)//发通知,调用的父类ForegroundService的方法
    }

    companion object {
        private const val HEAPDUMP_FILE_EXTRA = "HEAPDUMP_FILE_EXTRA"
        private const val HEAPDUMP_DURATION_MILLIS_EXTRA = "HEAPDUMP_DURATION_MILLIS_EXTRA"
        private const val HEAPDUMP_REASON_EXTRA = "HEAPDUMP_REASON_EXTRA"
        private const val PROGUARD_MAPPING_FILE_NAME = "leakCanaryObfuscationMapping.txt"

        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 {
                //let函数默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return。
                //通过let语句,在?.let之后,如果为空不会有任何操作,只有在非空的时候才会执行let之后的操作
                intent.putExtra(HEAPDUMP_DURATION_MILLIS_EXTRA, heapDumpDurationMillis)
            }
            startForegroundService(context, intent)
        }

        private fun startForegroundService(
                context: Context,
                intent: Intent
        ) {
            if (SDK_INT >= 26) {
                context.startForegroundService(intent)
            } else {
                // Pre-O behavior.
                context.startService(intent)
            }
        }
    }
}

8.22 Notifications发通知

internal object Notifications {

    //applicationVisible表示应用处于前台可见
    val canShowNotification: Boolean
        get() = canShowBackgroundNotifications || InternalLeakCanary.applicationVisible

    // Instant apps cannot show background notifications
    // See https://github.com/square/leakcanary/issues/1197
    // TV devices also can't do notifications
    //是MOBILE,并且不是isInstantApp
    private val canShowBackgroundNotifications =
            InternalLeakCanary.formFactor == MOBILE && !InternalLeakCanary.isInstantApp

    @Suppress("LongParameterList")
    fun showNotification(
            context: Context,
            contentTitle: CharSequence,
            contentText: CharSequence,
            pendingIntent: PendingIntent?,
            notificationId: Int,
            type: NotificationType
    ) {
        if (!canShowNotification) {
            return
        }

        val builder = if (SDK_INT >= O) {
            Notification.Builder(context, type.name)
        } else Notification.Builder(context)

        builder
                .setContentText(contentText)
                .setContentTitle(contentTitle)
                .setAutoCancel(true)
                .setContentIntent(pendingIntent)

        val notification =
                buildNotification(context, builder, type)
        val notificationManager =
                context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(notificationId, notification)
    }

    fun buildNotification(
            context: Context,
            builder: Notification.Builder,
            type: NotificationType
    ): Notification {
        builder.setSmallIcon(R.drawable.leak_canary_leak)
                .setWhen(System.currentTimeMillis())

        if (SDK_INT >= O) {
            val notificationManager =
                    context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            //anroid O 要notificationChannel
            var notificationChannel: NotificationChannel? =
                    notificationManager.getNotificationChannel(type.name)
            //通知channel频道
            if (notificationChannel == null) {
                val channelName = context.getString(type.nameResId)
                notificationChannel =
                        NotificationChannel(type.name, channelName, type.importance)
                notificationManager.createNotificationChannel(notificationChannel)
            }
            builder.setChannelId(type.name)
            builder.setGroup(type.name)
        }

        return if (SDK_INT < JELLY_BEAN) {
            @Suppress("DEPRECATION")
            builder.notification
        } else {
            builder.build()
        }
    }
}

8.23 HeapDumpTrigger判断是否可以dump heap,如果可以则dump,交与HeapAnalyzerService分析

//判断是否可以dump heap
// 如果都回收了不可以
// 没达到阈值并且可见,或者刚可见还没达到5s不可以
//最近dump还没超过60s则返回 不可以
// 可以dump,不可见的情况,返回后台了,就去dump,返回false,如果可以则dump,交与HeapAnalyzerService分析
internal class HeapDumpTrigger(
        private val application: Application,
        private val backgroundHandler: Handler,
        private val objectWatcher: ObjectWatcher,
        private val gcTrigger: GcTrigger,
        private val heapDumper: HeapDumper,
        private val configProvider: () -> Config
) {

    //NotificationManager
    private val notificationManager
        get() =
            application.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    //applicationInvisibleAt不可见的时候为-1,可见的时候为SystemClock.uptimeMillis
    private val applicationVisible
        get() = applicationInvisibleAt == -1L

    //下一次监测的具体时间点
    @Volatile
    private var checkScheduledAt: Long = 0L

    //最近监测的时候,泄漏的对象数量
    private var lastDisplayedRetainedObjectCount = 0

    //最近dump的时间
    private var lastHeapDumpUptimeMillis = 0L

    //1.dismissRetainedCountNotification 所有都被回收了,30s之后去除通知
    //2.%d retained objects, tap to dump heap 观察到有泄漏对象,30s之后去除通知
    private val scheduleDismissRetainedCountNotification = {
        dismissRetainedCountNotification()
    }

    //dismissNoRetainedOnTapNotification 手动dump,但没有泄漏对象,30s之后将通知去除
    private val scheduleDismissNoRetainedOnTapNotification = {
        dismissNoRetainedOnTapNotification()
    }

    /**
     * When the app becomes invisible, we don't dump the heap immediately. Instead we wait in case
     * the app came back to the foreground, but also to wait for new leaks that typically occur on
     * back press (activity destroy).
     *
     * 判断是否刚可见但是还没超过5s
     */
    private val applicationInvisibleLessThanWatchPeriod: Boolean
        get() {
            val applicationInvisibleAt = applicationInvisibleAt
            return applicationInvisibleAt != -1L && SystemClock.uptimeMillis() - applicationInvisibleAt < AppWatcher.config.watchDurationMillis
        }

    //可见的时间
    @Volatile
    private var applicationInvisibleAt = -1L

    //可见监听
    fun onApplicationVisibilityChanged(applicationVisible: Boolean) {
        if (applicationVisible) {
            applicationInvisibleAt = -1L
        } else {
            applicationInvisibleAt = SystemClock.uptimeMillis()
            // Scheduling for after watchDuration so that any destroyed activity has time to become
            // watch and be part of this analysis.
            //5s后调度以便任何被破坏的活动有时间成为 watch 并成为此分析的一部分。
            scheduleRetainedObjectCheck(
                    delayMillis = AppWatcher.config.watchDurationMillis//5s
            )
        }
    }

    private fun checkRetainedObjects() {
        //是否可以dump heap
        val iCanHasHeap = HeapDumpControl.iCanHasHeap()
        //配置
        val config = configProvider()
        //不可以的话,return
        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.
//                objectWatcher参数
                var retainedReferenceCount = objectWatcher.retainedObjectCount
                //主动触发gc
                if (retainedReferenceCount > 0) {
                    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))//手机啥也不干,tv弹toast
                    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
        }

        //如果没达到阈值,或者不可见/刚可见还没达到5s,返回

        if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

        //可以dump,就去dump
        val now = SystemClock.uptimeMillis()
        val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
        if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {//离最近的dump小于60s
            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"

        //开始dumpheap
        dumpHeap(
                retainedReferenceCount = retainedReferenceCount,
                retry = true,
                reason = "$retainedReferenceCount retained objects, app is $visibility"
        )
    }

    private fun dumpHeap(
            retainedReferenceCount: Int,
            retry: Boolean,
            reason: String
    ) {
        saveResourceIdNamesToMemory()//干哈的?todo
        val heapDumpUptimeMillis = SystemClock.uptimeMillis()
        KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis//dump开始的时间
        when (val heapDumpResult = heapDumper.dumpHeap()) {
            is NoHeapDump -> {
                //省略 dump失败,等待重试代码和发送失败通知代码
                if (retry) {
                    SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
                    scheduleRetainedObjectCheck(
                            delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS//5s
                    )
                } 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
                        )
                )
            }
            is HeapDump -> {
                lastDisplayedRetainedObjectCount = 0
                lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()//最近dump的时间
                ///清除 objectWatcher 中,在heapDumpUptimeMillis之前持有的对象,也就是已经dump的对象
                objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
                // 发送文件到HeapAnalyzerService解析
                HeapAnalyzerService.runAnalysis(
                        context = application,
                        heapDumpFile = heapDumpResult.file,
                        heapDumpDurationMillis = heapDumpResult.durationMillis,
                        heapDumpReason = reason
                )
            }
        }
    }

    //干哈的?todo
    private fun saveResourceIdNamesToMemory() {
        val resources = application.resources
        AndroidResourceIdNames.saveToMemory(
                getResourceTypeName = { id ->
                    try {
                        resources.getResourceTypeName(id)
                    } catch (e: NotFoundException) {
                        null
                    }
                },
                getResourceEntryName = { id ->
                    try {
                        resources.getResourceEntryName(id)
                    } catch (e: NotFoundException) {
                        null
                    }
                })
    }

    //用户手动dump
    fun onDumpHeapReceived(forceDump: Boolean) {
        backgroundHandler.post {//手动dump,放到后台线程
            dismissNoRetainedOnTapNotification()
            gcTrigger.runGc()
            val retainedReferenceCount = objectWatcher.retainedObjectCount
            if (!forceDump && retainedReferenceCount == 0) {//不是强制dump,并且数量为0的时候
                SharkLog.d { "Ignoring user request to dump heap: no retained objects remaining after GC" }
                @Suppress("DEPRECATION")
                val builder = Notification.Builder(application)
                        .setContentTitle(
                                application.getString(R.string.leak_canary_notification_no_retained_object_title)
                        )
                        .setContentText(
                                application.getString(
                                        R.string.leak_canary_notification_no_retained_object_content
                                )
                        )
                        .setAutoCancel(true)
                        .setContentIntent(NotificationReceiver.pendingIntent(application, CANCEL_NOTIFICATION))
                val notification =
                        Notifications.buildNotification(application, builder, LEAKCANARY_LOW)
                notificationManager.notify(
                        R.id.leak_canary_notification_no_retained_object_on_tap, notification
                )
                backgroundHandler.postDelayed(//通知
                        scheduleDismissNoRetainedOnTapNotification,
                        DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS
                )
                lastDisplayedRetainedObjectCount = 0
                return@post
            }

            SharkLog.d { "Dumping the heap because user requested it" }
            dumpHeap(retainedReferenceCount, retry = false, "user request")
        }
    }

    /**
     * 如果都回收了,(没达到阈值(并且可见,或者刚可见还没达到5s)),返回true,
     * 可以dump,不可见的情况,就去dump,返回false
     */
    private fun checkRetainedCount(
            retainedKeysCount: Int,
            retainedVisibleThreshold: Int,
            nopeReason: String? = null
    ): Boolean {
        val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
        lastDisplayedRetainedObjectCount = retainedKeysCount
        //都回收了
        if (retainedKeysCount == 0) {
            if (countChanged) {
                SharkLog.d { "All retained objects have been garbage collected" }
                onRetainInstanceListener.onEvent(NoMoreObjects)
                showNoMoreRetainedObjectNotification()
            }
            return true
        }

        val applicationVisible = applicationVisible
        val applicationInvisibleLessThanWatchPeriod = applicationInvisibleLessThanWatchPeriod

        //region 没啥用,log用
        if (countChanged) {
            val whatsNext = if (applicationVisible) {
                if (retainedKeysCount < retainedVisibleThreshold) {
                    "not dumping heap yet (app is visible & < $retainedVisibleThreshold threshold)"
                } else {
                    if (nopeReason != null) {
                        "would dump heap now (app is visible & >=$retainedVisibleThreshold threshold) but $nopeReason"
                    } else {
                        "dumping heap now (app is visible & >=$retainedVisibleThreshold threshold)"
                    }
                }
            } else if (applicationInvisibleLessThanWatchPeriod) {
                val wait =
                        AppWatcher.config.watchDurationMillis - (SystemClock.uptimeMillis() - applicationInvisibleAt)
                if (nopeReason != null) {
                    "would dump heap in $wait ms (app just became invisible) but $nopeReason"
                } else {
                    "dumping heap in $wait ms (app just became invisible)"
                }
            } else {
                if (nopeReason != null) {
                    "would dump heap now (app is invisible) but $nopeReason"
                } else {
                    "dumping heap now (app is invisible)"
                }
            }

            SharkLog.d {
                val s = if (retainedKeysCount > 1) "s" else ""
                "Found $retainedKeysCount object$s retained, $whatsNext"
            }
        }
        //endregion

        if (retainedKeysCount < retainedVisibleThreshold) {
            if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
                if (countChanged) {
                    onRetainInstanceListener.onEvent(BelowThreshold(retainedKeysCount))
                }
                showRetainedCountNotification(
                        objectCount = retainedKeysCount,
                        contentText = application.getString(
                                R.string.leak_canary_notification_retained_visible, retainedVisibleThreshold
                        )
                )
                scheduleRetainedObjectCheck(
                        delayMillis = WAIT_FOR_OBJECT_THRESHOLD_MILLIS
                )
                return true
            }
        }
        return false
    }

    //delayMillis时间之后,
    fun scheduleRetainedObjectCheck(//5s
            delayMillis: Long = 0L
    ) {
        val checkCurrentlyScheduledAt = checkScheduledAt
        //下一次监测的具体时间点,如果>0,就是说有计划了,这里返回
        if (checkCurrentlyScheduledAt > 0) {
            return
        }
        checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
        backgroundHandler.postDelayed({//检测是否可以dump,放到后台线程
            checkScheduledAt = 0
            checkRetainedObjects()
        }, delayMillis)
    }

    //All retained objects have been garbage collected
    //所有都被回收了
    private fun showNoMoreRetainedObjectNotification() {
        backgroundHandler.removeCallbacks(scheduleDismissRetainedCountNotification)
        if (!Notifications.canShowNotification) {
            return
        }
        val builder = Notification.Builder(application)
                .setContentTitle(
                        application.getString(R.string.leak_canary_notification_no_retained_object_title)
                )
                .setContentText(
                        application.getString(
                                R.string.leak_canary_notification_no_retained_object_content
                        )
                )
                .setAutoCancel(true)
                .setContentIntent(NotificationReceiver.pendingIntent(application, CANCEL_NOTIFICATION))
        val notification =
                Notifications.buildNotification(application, builder, LEAKCANARY_LOW)
        notificationManager.notify(R.id.leak_canary_notification_retained_objects, notification)
        backgroundHandler.postDelayed(//通知
                scheduleDismissRetainedCountNotification, DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS
        )
    }

    //有待回收的对象
    private fun showRetainedCountNotification(
            objectCount: Int,
            contentText: String
    ) {
        backgroundHandler.removeCallbacks(scheduleDismissRetainedCountNotification)
        if (!Notifications.canShowNotification) {
            return
        }
        @Suppress("DEPRECATION")
        val builder = Notification.Builder(application)
                .setContentTitle(
                        //%d retained objects, tap to dump heap
                        application.getString(R.string.leak_canary_notification_retained_title, objectCount)
                )
                .setContentText(contentText)
                .setAutoCancel(true)
                .setContentIntent(NotificationReceiver.pendingIntent(application, DUMP_HEAP))
        val notification =
                Notifications.buildNotification(application, builder, LEAKCANARY_LOW)
        notificationManager.notify(R.id.leak_canary_notification_retained_objects, notification)
    }

    private fun dismissRetainedCountNotification() {
        backgroundHandler.removeCallbacks(scheduleDismissRetainedCountNotification)
        notificationManager.cancel(R.id.leak_canary_notification_retained_objects)
    }

    private fun dismissNoRetainedOnTapNotification() {
        backgroundHandler.removeCallbacks(scheduleDismissNoRetainedOnTapNotification)
        notificationManager.cancel(R.id.leak_canary_notification_no_retained_object_on_tap)
    }

    companion object {
        private const val WAIT_AFTER_DUMP_FAILED_MILLIS = 5_000L
        private const val WAIT_FOR_OBJECT_THRESHOLD_MILLIS = 2_000L
        private const val DISMISS_NO_RETAINED_OBJECT_NOTIFICATION_MILLIS = 30_000L
        private const val WAIT_BETWEEN_HEAP_DUMPS_MILLIS = 60_000L
    }
}

8.24 InternalLeakCanary 创建后台线程,交与HeapDumpTrigger定时判断是否需要dump,AndroidHeapDumper负责dump,交与HeapAnalyzerService分析


internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    //用于添加动态图标
    private const val DYNAMIC_SHORTCUT_ID = "com.squareup.leakcanary.dynamic_shortcut"

    private lateinit var heapDumpTrigger: HeapDumpTrigger//lateinit 只用于变量 var,而 lazy 只用于常量 val,https://www.jianshu.com/p/e2cb4c65d4ff

    // You're wrong https://discuss.kotlinlang.org/t/object-or-top-level-property-name-warning/6621/7
    @Suppress("ObjectPropertyName")
    private var _application: Application? = null

    val application: Application
        get() {
            check(_application != null) {
                "LeakCanary not installed, see AppWatcher.manualInstall()"
            }
            return _application!!
        }

    // BuildConfig.LIBRARY_VERSION is stripped so this static var is how we keep it around to find
    // it later when parsing the heap dump.
    @Suppress("unused")
    @JvmStatic
    private var version = BuildConfig.LIBRARY_VERSION

    @Volatile
    var applicationVisible = false
        private set

    private val isDebuggableBuild by lazy {
        (application.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
    }

    fun createLeakDirectoryProvider(context: Context): LeakDirectoryProvider {
        val appContext = context.applicationContext
        return LeakDirectoryProvider(appContext, {
            LeakCanary.config.maxStoredHeapDumps//7
        }, {
            LeakCanary.config.requestWriteExternalStoragePermission//false
        })
    }

    //型号类型
    internal enum class FormFactor {
        MOBILE,
        TV,
        WATCH,
    }

    //获取是手机吗
    val formFactor by lazy {
        val currentModeType =
                (application.getSystemService(UI_MODE_SERVICE) as UiModeManager).currentModeType
        return@lazy when (currentModeType) {
            Configuration.UI_MODE_TYPE_TELEVISION -> TV
            Configuration.UI_MODE_TYPE_WATCH -> WATCH
            else -> MOBILE
        }
    }

    //是否是isInstantApp
    val isInstantApp by lazy {
        VERSION.SDK_INT >= VERSION_CODES.O && application.packageManager.isInstantApp
    }

    //onRetainInstanceListener通知
    val onRetainInstanceListener by lazy {
        when (formFactor) {
            TV -> TvOnRetainInstanceListener(application)//tv的话有
            else -> DefaultOnRetainInstanceListener()//手机的话不干啥
        }
    }

    //可见的activity
    var resumedActivity: Activity? = null

    //sp
    private val heapDumpPrefs by lazy {
        application.getSharedPreferences("LeakCanaryHeapDumpPrefs", Context.MODE_PRIVATE)
    }

    //about页面的控制
    internal var dumpEnabledInAboutScreen: Boolean
        get() {
            return heapDumpPrefs
                    .getBoolean("AboutScreenDumpEnabled", true)
        }
        set(value) {
            heapDumpPrefs
                    .edit()
                    .putBoolean("AboutScreenDumpEnabled", value)
                    .apply()
        }

    override fun invoke(application: Application) {
        _application = application
        //只能运行在debug版本,release可以配置
        checkRunningInDebuggableBuild()
        //添加 addOnObjectRetainedListener,泄漏发生的时候调用onObjectRetained
        AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
        //dump真正的地方AndroidHeapDumper,将其传给HeapDumpTrigger管理
        val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))
        //Gc触发器
        val gcTrigger = GcTrigger.Default
        //config
        val configProvider = { LeakCanary.config }
        //开辟一个线程,在后台监测是否可以dump
        val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
        handlerThread.start()
        val backgroundHandler = Handler(handlerThread.looper)
        //创建内存快照转储触发器 HeapDumpTrigger
        heapDumpTrigger = HeapDumpTrigger(
                application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
                configProvider
        )
        //添加application前后台变化监听
        //监听application 前后台变动,并且记录来到后台时间,便于LeakCanary 针对刚刚切入后台的一些destroy操作做泄漏监测
        application.registerVisibilityListener { applicationVisible ->
            this.applicationVisible = applicationVisible
            heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
        }
        //注册activity生命周期回调,获取当前resumed的activity实例
        registerResumedActivityListener(application)
        //添加动态的桌面快捷入口
        addDynamicShortcut(application)

        // We post so that the log happens after Application.onCreate()
        // 判断是否应该DumpHeap
        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()
                        )
                    }
                }
            }
        }
    }

    private fun checkRunningInDebuggableBuild() {
        if (isDebuggableBuild) {
            return
        }

        if (!application.resources.getBoolean(R.bool.leak_canary_allow_in_non_debuggable_build)) {
            throw Error(
                    """
        LeakCanary in non-debuggable build
        
        LeakCanary should only be used in debug builds, but this APK is not debuggable.
        Please follow the instructions on the "Getting started" page to only include LeakCanary in
        debug builds: https://square.github.io/leakcanary/getting_started/
        
        If you're sure you want to include LeakCanary in a non-debuggable build, follow the 
        instructions here: https://square.github.io/leakcanary/recipes/#leakcanary-in-release-builds
      """.trimIndent()
            )
        }
    }

    private fun registerResumedActivityListener(application: Application) {
        application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks by noOpDelegate() {
            override fun onActivityResumed(activity: Activity) {
                resumedActivity = activity
            }

            override fun onActivityPaused(activity: Activity) {
                if (resumedActivity === activity) {
                    resumedActivity = null
                }
            }
        })
    }

    //动态图标,长安点击出来的那个
    @Suppress("ReturnCount")
    private fun addDynamicShortcut(application: Application) {
        if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
            return
        }
        if (!application.resources.getBoolean(R.bool.leak_canary_add_dynamic_shortcut)) {
            return
        }
        if (isInstantApp) {
            // Instant Apps don't have access to ShortcutManager
            return
        }

        val shortcutManager = application.getSystemService(ShortcutManager::class.java)!!
        val dynamicShortcuts = shortcutManager.dynamicShortcuts

        val shortcutInstalled =
                dynamicShortcuts.any { shortcut -> shortcut.id == DYNAMIC_SHORTCUT_ID }

        if (shortcutInstalled) {
            return
        }

        val mainIntent = Intent(Intent.ACTION_MAIN, null)
        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER)
        mainIntent.setPackage(application.packageName)
        val activities = application.packageManager.queryIntentActivities(mainIntent, 0)
                .filter {
                    it.activityInfo.name != "leakcanary.internal.activity.LeakLauncherActivity"
                }

        if (activities.isEmpty()) {//app
            return
        }

        val firstMainActivity = activities.first()
                .activityInfo

        // Displayed on long tap on app icon
        val longLabel: String
        // Label when dropping shortcut to launcher
        val shortLabel: String

        val leakActivityLabel = application.getString(R.string.leak_canary_shortcut_label)

        if (activities.isEmpty()) {
            longLabel = leakActivityLabel
            shortLabel = leakActivityLabel
        } else {
            val firstLauncherActivityLabel = if (firstMainActivity.labelRes != 0) {
                application.getString(firstMainActivity.labelRes)
            } else {
                application.packageManager.getApplicationLabel(application.applicationInfo)
            }
            val fullLengthLabel = "$firstLauncherActivityLabel $leakActivityLabel"
            // short label should be under 10 and long label under 25
            if (fullLengthLabel.length > 10) {
                if (fullLengthLabel.length <= 25) {
                    longLabel = fullLengthLabel
                    shortLabel = leakActivityLabel
                } else {
                    longLabel = leakActivityLabel
                    shortLabel = leakActivityLabel
                }
            } else {
                longLabel = fullLengthLabel
                shortLabel = fullLengthLabel
            }
        }

        val componentName = ComponentName(firstMainActivity.packageName, firstMainActivity.name)

        val shortcutCount = dynamicShortcuts.count { shortcutInfo ->
            shortcutInfo.activity == componentName
        } + shortcutManager.manifestShortcuts.count { shortcutInfo ->
            shortcutInfo.activity == componentName
        }

        if (shortcutCount >= shortcutManager.maxShortcutCountPerActivity) {
            return
        }

        val intent = LeakCanary.newLeakDisplayActivityIntent()
        intent.action = "Dummy Action because Android is stupid"
        val shortcut = Builder(application, DYNAMIC_SHORTCUT_ID)
                .setLongLabel(longLabel)
                .setShortLabel(shortLabel)
                .setActivity(componentName)
                .setIcon(Icon.createWithResource(application, R.mipmap.leak_canary_icon))
                .setIntent(intent)
                .build()

        try {
            shortcutManager.addDynamicShortcuts(listOf(shortcut))
        } catch (ignored: Throwable) {
            SharkLog.d(ignored) {
                "Could not add dynamic shortcut. " +
                        "shortcutCount=$shortcutCount, " +
                        "maxShortcutCountPerActivity=${shortcutManager.maxShortcutCountPerActivity}"
            }
        }
    }

    //发现泄漏了,调用heapDumpTrigger的scheduleRetainedObjectCheck
    override fun onObjectRetained() = scheduleRetainedObjectCheck()

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

    //用户手动触发dump
    fun onDumpHeapReceived(forceDump: Boolean) {
        if (this::heapDumpTrigger.isInitialized) {
            heapDumpTrigger.onDumpHeapReceived(forceDump)
        }
    }

    //显示/隐藏快捷键快捷方法
    fun setEnabledBlocking(//nouse,没有调用到
            componentClassName: String,
            enabled: Boolean
    ) {
        val component = ComponentName(application, componentClassName)
        val newState =
                if (enabled) COMPONENT_ENABLED_STATE_ENABLED else COMPONENT_ENABLED_STATE_DISABLED
        // Blocks on IPC.
        application.packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP)
    }

    private const val LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump"
}

8.25 LeakCanary Config 配置,和几个公共方法newLeakDisplayActivityIntent showLeakDisplayActivityLauncherIcon dumpHeap

object LeakCanary {

    /**
     * LeakCanary configuration data class. Properties can be updated via [copy].
     *
     * @see [config]
     */
    data class Config(
            /**
             * Whether LeakCanary should dump the heap when enough retained instances are found. This needs
             * to be true for LeakCanary to work, but sometimes you may want to temporarily disable
             * LeakCanary (e.g. for a product demo).
             *
             * Defaults to true.
             */
            val dumpHeap: Boolean = true,//控制是否dump
            /**
             * If [dumpHeapWhenDebugging] is false then LeakCanary will not dump the heap
             * when the debugger is attached. The debugger can create temporary memory leaks (for instance
             * if a thread is blocked on a breakpoint).
             *
             * Defaults to false.
             */
            val dumpHeapWhenDebugging: Boolean = false,//控制debugger模式是否dump
            /**
             * When the app is visible, LeakCanary will wait for at least
             * [retainedVisibleThreshold] retained instances before dumping the heap. Dumping the heap
             * freezes the UI and can be frustrating for developers who are trying to work. This is
             * especially frustrating as the Android Framework has a number of leaks that cannot easily
             * be fixed.
             *
             * When the app becomes invisible, LeakCanary dumps the heap after
             * [AppWatcher.Config.watchDurationMillis] ms.
             *
             * The app is considered visible if it has at least one activity in started state.
             *
             * A higher threshold means LeakCanary will dump the heap less often, therefore it won't be
             * bothering developers as much but it could miss some leaks.
             *
             * Defaults to 5.
             */
            val retainedVisibleThreshold: Int = 5,//数量阈值5

            /**
             * Known patterns of references in the heap, added here either to ignore them
             * ([IgnoredReferenceMatcher]) or to mark them as library leaks ([LibraryLeakReferenceMatcher]).
             *
             * When adding your own custom [LibraryLeakReferenceMatcher] instances, you'll most
             * likely want to set [LibraryLeakReferenceMatcher.patternApplies] with a filter that checks
             * for the Android OS version and manufacturer. The build information can be obtained by calling
             * [shark.AndroidBuildMirror.fromHeapGraph].
             *
             * Defaults to [AndroidReferenceMatchers.appDefaults]
             *
             * todo
             */
            val referenceMatchers: List<ReferenceMatcher> = AndroidReferenceMatchers.appDefaults,

            /**
             * List of [ObjectInspector] that provide LeakCanary with insights about objects found in the
             * heap. You can create your own [ObjectInspector] implementations, and also add
             * a [shark.AppSingletonInspector] instance created with the list of internal singletons.
             *
             * Defaults to [AndroidObjectInspectors.appDefaults]
             *
             * todo
             */
            val objectInspectors: List<ObjectInspector> = AndroidObjectInspectors.appDefaults,

            /**
             * Called on a background thread when the heap analysis is complete.
             * If you want leaks to be added to the activity that lists leaks, make sure to delegate
             * calls to a [DefaultOnHeapAnalyzedListener].
             *
             * Defaults to [DefaultOnHeapAnalyzedListener]
             *
             * 默认是DefaultOnHeapAnalyzedListener,后台线程分析完调用,将leaks加入到数据库中,
             * 并发出notification,点击notification去LeakActivity
             */
            val onHeapAnalyzedListener: OnHeapAnalyzedListener = DefaultOnHeapAnalyzedListener.create(),

            /**
             * Extracts metadata from a hprof to be reported in [HeapAnalysisSuccess.metadata].
             * Called on a background thread during heap analysis.
             *
             * Defaults to [AndroidMetadataExtractor]
             * todo
             */
            val metadataExtractor: MetadataExtractor = AndroidMetadataExtractor,

            /**
             * Whether to compute the retained heap size, which is the total number of bytes in memory that
             * would be reclaimed if the detected leaks didn't happen. This includes native memory
             * associated to Java objects (e.g. Android bitmaps).
             *
             * Computing the retained heap size can slow down the analysis because it requires navigating
             * from GC roots through the entire object graph, whereas [shark.HeapAnalyzer] would otherwise
             * stop as soon as all leaking instances are found.
             *
             * Defaults to true.
             * 会耗时,计算泄漏的内存大小需要从GC根导航到整个对象图,
             * 而[shark.HeapAnalyzer]会在找到所有泄漏实例后立即停止。
             */
            val computeRetainedHeapSize: Boolean = true,

            /**
             * How many heap dumps are kept on the Android device for this app package. When this threshold
             * is reached LeakCanary deletes the older heap dumps. As several heap dumps may be enqueued
             * you should avoid going down to 1 or 2.
             *
             * Defaults to 7.
             * 保持的heap个数,7个,不要设置为1,2个
             */
            val maxStoredHeapDumps: Int = 7,

            /**
             * LeakCanary always attempts to store heap dumps on the external storage if the
             * WRITE_EXTERNAL_STORAGE is already granted, and otherwise uses the app storage.
             * If the WRITE_EXTERNAL_STORAGE permission is not granted and
             * [requestWriteExternalStoragePermission] is true, then LeakCanary will display a notification
             * to ask for that permission.
             *
             * Defaults to false because that permission notification can be annoying.
             * 如果没权限,requestWriteExternalStoragePermission为true的时候,会显示通知
             */
            val requestWriteExternalStoragePermission: Boolean = false,

            /**
             * Finds the objects that are leaking, for which LeakCanary will compute leak traces.
             *
             * Defaults to [KeyedWeakReferenceFinder] which finds all objects tracked by a
             * [KeyedWeakReference], ie all objects that were passed to
             * [ObjectWatcher.expectWeaklyReachable].
             *
             * You could instead replace it with a [FilteringLeakingObjectFinder], which scans all objects
             * in the heap dump and delegates the decision to a list of
             * [FilteringLeakingObjectFinder.LeakingObjectFilter]. This can lead to finding more leaks
             * than the default and shorter leak traces. This also means that every analysis during a
             * given process life will bring up the same leaking objects over and over again, unlike
             * when using [KeyedWeakReferenceFinder] (because [KeyedWeakReference] instances are cleared
             * after each heap dump).
             *
             * The list of filters can be built from [AndroidObjectInspectors]:
             *
             * ```
             * LeakCanary.config = LeakCanary.config.copy(
             *     leakingObjectFinder = FilteringLeakingObjectFinder(
             *         AndroidObjectInspectors.appLeakingObjectFilters
             *     )
             * )
             * ```
             *
             * todo
             */
            val leakingObjectFinder: LeakingObjectFinder = KeyedWeakReferenceFinder,

            /**
             * Deprecated: This is a no-op, set a custom [leakingObjectFinder] instead.
             */
            @Deprecated("This is a no-op, set a custom leakingObjectFinder instead")
            val useExperimentalLeakFinders: Boolean = false
    ) {

        /**
         * Construct a new Config via [LeakCanary.Config.Builder].
         * Note: this method is intended to be used from Java code only. For idiomatic(惯用的) Kotlin use
         * `copy()` to modify [LeakCanary.config].
         */
        @Suppress("NEWER_VERSION_IN_SINCE_KOTLIN")
        @SinceKotlin("999.9") // Hide from Kotlin code, this method is only for Java code
        fun newBuilder() = Builder(this)

        /**
         * Builder for [LeakCanary.Config] intended to be used only from Java code.
         *
         * Usage:
         * ```
         * LeakCanary.Config config = LeakCanary.getConfig().newBuilder()
         *    .retainedVisibleThreshold(3)
         *    .build();
         * LeakCanary.setConfig(config);
         * ```
         *
         * For idiomatic Kotlin use `copy()` method instead:
         * ```
         * LeakCanary.config = LeakCanary.config.copy(retainedVisibleThreshold = 3)
         * ```
         */
        class Builder internal constructor(config: Config) {
            private var dumpHeap = config.dumpHeap
            private var dumpHeapWhenDebugging = config.dumpHeapWhenDebugging
            private var retainedVisibleThreshold = config.retainedVisibleThreshold
            private var referenceMatchers = config.referenceMatchers
            private var objectInspectors = config.objectInspectors
            private var onHeapAnalyzedListener = config.onHeapAnalyzedListener
            private var metadataExtractor = config.metadataExtractor
            private var computeRetainedHeapSize = config.computeRetainedHeapSize
            private var maxStoredHeapDumps = config.maxStoredHeapDumps
            private var requestWriteExternalStoragePermission =
                    config.requestWriteExternalStoragePermission
            private var leakingObjectFinder = config.leakingObjectFinder

            /** @see [LeakCanary.Config.dumpHeap] */
            fun dumpHeap(dumpHeap: Boolean) =
                    apply { this.dumpHeap = dumpHeap }

            /** @see [LeakCanary.Config.dumpHeapWhenDebugging] */
            fun dumpHeapWhenDebugging(dumpHeapWhenDebugging: Boolean) =
                    apply { this.dumpHeapWhenDebugging = dumpHeapWhenDebugging }

            /** @see [LeakCanary.Config.retainedVisibleThreshold] */
            fun retainedVisibleThreshold(retainedVisibleThreshold: Int) =
                    apply { this.retainedVisibleThreshold = retainedVisibleThreshold }

            /** @see [LeakCanary.Config.referenceMatchers] */
            fun referenceMatchers(referenceMatchers: List<ReferenceMatcher>) =
                    apply { this.referenceMatchers = referenceMatchers }

            /** @see [LeakCanary.Config.objectInspectors] */
            fun objectInspectors(objectInspectors: List<ObjectInspector>) =
                    apply { this.objectInspectors = objectInspectors }

            /** @see [LeakCanary.Config.onHeapAnalyzedListener] */
            fun onHeapAnalyzedListener(onHeapAnalyzedListener: OnHeapAnalyzedListener) =
                    apply { this.onHeapAnalyzedListener = onHeapAnalyzedListener }

            /** @see [LeakCanary.Config.metadataExtractor] */
            fun metadataExtractor(metadataExtractor: MetadataExtractor) =
                    apply { this.metadataExtractor = metadataExtractor }

            /** @see [LeakCanary.Config.computeRetainedHeapSize] */
            fun computeRetainedHeapSize(computeRetainedHeapSize: Boolean) =
                    apply { this.computeRetainedHeapSize = computeRetainedHeapSize }

            /** @see [LeakCanary.Config.maxStoredHeapDumps] */
            fun maxStoredHeapDumps(maxStoredHeapDumps: Int) =
                    apply { this.maxStoredHeapDumps = maxStoredHeapDumps }

            /** @see [LeakCanary.Config.requestWriteExternalStoragePermission] */
            fun requestWriteExternalStoragePermission(requestWriteExternalStoragePermission: Boolean) =
                    apply { this.requestWriteExternalStoragePermission = requestWriteExternalStoragePermission }

            /** @see [LeakCanary.Config.leakingObjectFinder] */
            fun leakingObjectFinder(leakingObjectFinder: LeakingObjectFinder) =
                    apply { this.leakingObjectFinder = leakingObjectFinder }

            fun build() = config.copy(
                    dumpHeap = dumpHeap,
                    dumpHeapWhenDebugging = dumpHeapWhenDebugging,
                    retainedVisibleThreshold = retainedVisibleThreshold,
                    referenceMatchers = referenceMatchers,
                    objectInspectors = objectInspectors,
                    onHeapAnalyzedListener = onHeapAnalyzedListener,
                    metadataExtractor = metadataExtractor,
                    computeRetainedHeapSize = computeRetainedHeapSize,
                    maxStoredHeapDumps = maxStoredHeapDumps,
                    requestWriteExternalStoragePermission = requestWriteExternalStoragePermission,
                    leakingObjectFinder = leakingObjectFinder
            )
        }
    }

    /**
     * The current LeakCanary configuration. Can be updated at any time, usually by replacing it with
     * a mutated copy, e.g.:
     *
     * ```
     * LeakCanary.config = LeakCanary.config.copy(retainedVisibleThreshold = 3)
     * ```
     *
     * In Java, use [LeakCanary.Config.Builder] instead:
     * ```
     * LeakCanary.Config config = LeakCanary.getConfig().newBuilder()
     *    .retainedVisibleThreshold(3)
     *    .build();
     * LeakCanary.setConfig(config);
     * ```
     */
    @JvmStatic
    @Volatile
    var config: Config = Config()
        set(newConfig) {
            val previousConfig = field
            field = newConfig
            logConfigChange(previousConfig, newConfig)
            HeapDumpControl.updateICanHasHeap()
        }

    //打印config区别
    private fun logConfigChange(
            previousConfig: Config,
            newConfig: Config
    ) {
        SharkLog.d {
            val changedFields = mutableListOf<String>()
            Config::class.java.declaredFields.forEach { field ->
                field.isAccessible = true
                val previousValue = field[previousConfig]
                val newValue = field[newConfig]
                if (previousValue != newValue) {
                    changedFields += "${field.name}=$newValue"
                }
            }
            val changesInConfig =
                    if (changedFields.isNotEmpty()) changedFields.joinToString(", ") else "no changes"

            "Updated LeakCanary.config: Config($changesInConfig)"
        }
    }

    /**
     * Returns a new [Intent] that can be used to programmatically launch the leak display activity.
     * 跳转LeakActivity的方法
     */
    fun newLeakDisplayActivityIntent() = LeakActivity.createIntent(InternalLeakCanary.application)

    /**
     * Dynamically shows / hides the launcher icon for the leak display activity.
     * Note: you can change the default value by overriding the `leak_canary_add_launcher_icon`
     * boolean resource:
     *
     * ```
     * <?xml version="1.0" encoding="utf-8"?>
     * <resources>
     *   <bool name="leak_canary_add_launcher_icon">false</bool>
     * </resources>
     * ```
     *
     * 显示/隐藏快捷键快捷方法
     */
    fun showLeakDisplayActivityLauncherIcon(showLauncherIcon: Boolean) {
        InternalLeakCanary.setEnabledBlocking(
                "leakcanary.internal.activity.LeakLauncherActivity", showLauncherIcon
        )
    }

    /**
     * Immediately triggers a heap dump and analysis, if there is at least one retained instance
     * tracked by [AppWatcher.objectWatcher]. If there are no retained instances then the heap will not
     * be dumped and a notification will be shown instead.
     *
     * 手动触发dump快捷方法,forceDump强制dump
     */
    fun dumpHeap() = InternalLeakCanary.onDumpHeapReceived(forceDump = true)
}

8.26 DisplayLeakAdapter 显示引用连列表


@Suppress("DEPRECATION")
internal class DisplayLeakAdapter constructor(
        context: Context,
        private val leakTrace: LeakTrace,
        private val header: CharSequence//
) : BaseAdapter() {

    private val highlightColorHexString: String
    private val leakColorHexString: String//#BE383F
    private val referenceColorHexString: String//#9976A8
    private val extraColorHexString: String
    private val helpColorHexString: String

    init {
        highlightColorHexString = hexStringColor(context, R.color.leak_canary_class_name)
        leakColorHexString = hexStringColor(context, R.color.leak_canary_leak)
        referenceColorHexString = hexStringColor(context, R.color.leak_canary_reference)
        extraColorHexString = hexStringColor(context, R.color.leak_canary_extra)
        helpColorHexString = hexStringColor(context, R.color.leak_canary_help)
    }

    //OK 类型就两种,header,和 row
    override fun getView(
            position: Int,
            convertView: View?,
            parent: ViewGroup
    ): View {
        return when (getItemViewType(position)) {
            HEADER_ROW -> {//第1个
                val view = convertView ?: parent.inflate(R.layout.leak_canary_leak_header)
                bindHeaderRow(view)
                view
            }
            CONNECTOR_ROW -> {//第2个到最后一个
                val view = convertView ?: parent.inflate(R.layout.leak_canary_ref_row)
                bindConnectorRow(view, position)
                view
            }
            else -> {
                throw IllegalStateException("Unexpected type ${getItemViewType(position)}")
            }
        }
    }

    //ok 绑定header
    private fun bindHeaderRow(//第1个
            view: View
    ) {
        view.findViewById<TextView>(R.id.leak_canary_header_text)
                .apply {
                    //图文//https://www.jianshu.com/p/6843f332c8df
                    //formHtml()方法已经将 HTML 内容中的超链接和图片转义成为 UrlSpan 和 ImageSpan,进而在 TextView 中完成显示。
                    // 但是此时是没有任何用户交互的,用户只能看到 HTML 的内容,下面介绍如何添加用户交互功能。
                    //
                    //要完成用户交互,这里我们需要在 TextView 中还需要调用textView.setMovementMethod()方法。
                    //
                    //Android 提供了 LinkMovementMethod 类以实现了对于文本内容中超链接的遍历,并且支持对于超链接的点击事件。
                    movementMethod = LinkMovementMethod.getInstance()
                    text = header
                }
    }

    //绑定每行
    private fun bindConnectorRow(//第2个到最后一个
            view: View,
            position: Int
    ) {
        val titleView = view.findViewById<TextView>(R.id.leak_canary_row_title)
        val connector = view.findViewById<DisplayLeakConnectorView>(R.id.leak_canary_row_connector)

        //设置指针类型
        connector.setType(getConnectorType(position))

        titleView.text = when {
            position == 1 -> {//第二个,index为1,第一个显示head了
                "GC Root: ${leakTrace.gcRootType.description}"
            }
            position < count - 1 -> {//
                val referencePathIndex = elementIndex(position)
                //LeakTraceReference 引用连其中一个path
                val referencePath = leakTrace.referencePath[referencePathIndex]
                //是否怀疑泄漏
                val isSuspect = leakTrace.referencePathElementIsSuspect(referencePathIndex)
                //LeakTraceObject 引用连的对象
                val leakTraceObject = referencePath.originObject
                //typeName
                //      thread或者
                //      CLASS,
                //      ARRAY,
                //      INSTANCE
                val typeName =
                        if (position == 2 && leakTrace.gcRootType == JAVA_FRAME) "thread" else leakTraceObject.typeName
                //对象名字
                var referenceName = referencePath.referenceDisplayName

                referenceName = referenceName.replace("<".toRegex(), "&lt;")
                        .replace(">".toRegex(), "&gt;")

                referenceName = if (isSuspect) {//颜色
                    Log.i("LeakCanary", "DisplayLeakAdapter leakColorHexString " + leakColorHexString)
                    "<u><font color='$leakColorHexString'>$referenceName</font></u>"
                } else {
                    Log.i("LeakCanary", "DisplayLeakAdapter referenceColorHexString " + referenceColorHexString)
                    "<font color='$referenceColorHexString'>$referenceName</font>"
                }

                if (referencePath.referenceType == STATIC_FIELD) {
                    referenceName = "<i>$referenceName</i>"//斜体文本。静态的呈现斜体
                }

                if (isSuspect) {
                    referenceName = "<b>$referenceName</b>"//如果怀疑的话加粗
                }
                //是否静态
                val staticPrefix = if (referencePath.referenceType == STATIC_FIELD) "static " else ""
                //空格 + 静态 + 类名 + 对象名字
                val htmlString = leakTraceObject.asHtmlString(typeName) +
                        "$INDENTATION$staticPrefix${referencePath.styledOwningClassSimpleName()}.$referenceName"

                val builder = Html.fromHtml(htmlString) as SpannableStringBuilder
                if (isSuspect) {
                    SquigglySpan.replaceUnderlineSpans(builder, view.context)//加上斜线
                }
                builder
            }//第三个到 count-2,倒数第二个
            else -> {//最后一个
                Html.fromHtml(leakTrace.leakingObject.asHtmlString(leakTrace.leakingObject.typeName))
            }
        }
    }

    //
    private fun LeakTraceObject.asHtmlString(typeName: String): String {
        val packageEnd = className.lastIndexOf('.')

        val extra: (String) -> String = { "<font color='$extraColorHexString'>$it</font>" }

        val styledClassName = styledClassSimpleName()
        var htmlString =
                if (packageEnd != -1) "${
                    extra(
                            className.substring(
                                    0, packageEnd
                            )
                    )
                }.$styledClassName" else styledClassName
        //包名+类名+ typename
        htmlString += " ${extra(typeName)}<br>"

        val reachabilityString = when (leakingStatus) {
            UNKNOWN -> extra("UNKNOWN")
            NOT_LEAKING -> "NO" + extra(" (${leakingStatusReason})")
            LEAKING -> "YES" + extra(" (${leakingStatusReason})")
        }

        //是否泄漏
        htmlString += "$INDENTATION${extra("Leaking: ")}$reachabilityString<br>"

        //泄漏的多少内存多少个对象
        retainedHeapByteSize?.let {
            val humanReadableRetainedHeapSize = humanReadableByteCount(it.toLong(), si = true)
            htmlString += "${INDENTATION}${extra("Retaining ")}$humanReadableRetainedHeapSize${
                extra(
                        " in "
                )
            }$retainedObjectCount${extra(" objects")}<br>"
        }

        //一些其他参数
        labels.forEach { label ->//key,watchDurationMillis,retainedDurationMillis,mApplication,mBase
            htmlString += "$INDENTATION${extra(label)}<br>"
        }
        return htmlString
    }

    private fun LeakTraceObject.styledClassSimpleName(): String {//扩展方法
        val simpleName = classSimpleName.replace("[]", "[ ]")
        return "<font color='$highlightColorHexString'>$simpleName</font>"
    }

    private fun LeakTraceReference.styledOwningClassSimpleName(): String {
        val simpleName = owningClassSimpleName.replace("[]", "[ ]")
        return "<font color='$highlightColorHexString'>$simpleName</font>"
    }

    //获取前面指针的类型
    @Suppress("ReturnCount")
    private fun getConnectorType(position: Int): Type {
        if (position == 1) {
            return GC_ROOT
        } else if (position == 2) {
            return when (leakTrace.referencePath.size) {
                0 -> END_FIRST_UNREACHABLE
                1 -> START_LAST_REACHABLE
                else -> {
                    val nextReachability = leakTrace.referencePath[1].originObject
                    if (nextReachability.leakingStatus != NOT_LEAKING) {
                        START_LAST_REACHABLE
                    } else START
                }
            }
        } else {
            val isLeakingInstance = position == count - 1
            if (isLeakingInstance) {
                val previousReachability = leakTrace.referencePath.last()
                        .originObject
                return if (previousReachability.leakingStatus != LEAKING) {
                    END_FIRST_UNREACHABLE
                } else END
            } else {
                val reachability = leakTrace.referencePath[elementIndex(position)].originObject
                when (reachability.leakingStatus) {
                    UNKNOWN -> return NODE_UNKNOWN
                    NOT_LEAKING -> {
                        val nextReachability =
                                if (position + 1 == count - 1) leakTrace.leakingObject else leakTrace.referencePath[elementIndex(
                                        position + 1
                                )].originObject
                        return if (nextReachability.leakingStatus != NOT_LEAKING) {
                            NODE_LAST_REACHABLE
                        } else {
                            NODE_REACHABLE
                        }
                    }
                    LEAKING -> {
                        val previousReachability =
                                leakTrace.referencePath[elementIndex(position - 1)].originObject
                        return if (previousReachability.leakingStatus != LEAKING) {
                            NODE_FIRST_UNREACHABLE
                        } else {
                            NODE_UNREACHABLE
                        }
                    }
                    else -> throw IllegalStateException(
                            "Unknown value: " + reachability.leakingStatus
                    )
                }
            }
        }
    }

    override fun isEnabled(position: Int) = false

    override fun getCount() = leakTrace.referencePath.size + 3//header + GC Root + 最后一个

    override fun getItem(position: Int) = when {
        position == 0 || position == 1 -> null              //前两个
        position == count - 1 -> leakTrace.leakingObject    //最后一个
        else -> leakTrace.referencePath[elementIndex(position)]//中间的显示leakTrace.referencePath里的
    }

    private fun elementIndex(position: Int) = position - 2//中间的

    override fun getViewTypeCount() = 2

    override fun getItemViewType(position: Int) = if (position == 0) HEADER_ROW else CONNECTOR_ROW

    override fun getItemId(position: Int) = position.toLong()

    companion object {

        const val HEADER_ROW = 0
        const val CONNECTOR_ROW = 1

        //在html代码中每输入一个转义字符&nbsp就表示一个空格,输入十个&nbsp,页面中就显示10个空格位置。
        //而在html代码中输入空格,不管输入多少个空格,最终在页面中显示的空格位置只有一个。
        val INDENTATION = "&nbsp;".repeat(4)//indentation行首空格

        // https://stackoverflow.com/a/6540378/703646 十六进制
        private fun hexStringColor(
                context: Context,
                colorResId: Int
        ): String {
            //使用的html
            return String.format("#%06X", 0xFFFFFF and context.getColorCompat(colorResId))
        }
    }
}

8.27 Screen接口 代表页面,显示一个view

//Screen 代表页面,显示一个view
internal abstract class Screen : Serializable {

  abstract fun createView(container: ViewGroup): View
}

8.28 Views.kt 定义了一些扩展函数 inflat,activity,goto,toback,onCreateOptionsMenu,onScreenExiting,notifyScreenExiting

//定义了一些扩展函数
internal fun ViewGroup.inflate(layoutResId: Int) = LayoutInflater.from(context)
        .inflate(layoutResId, this, false)!!

internal val View.activity
    get() = context as Activity

@Suppress("UNCHECKED_CAST")
internal fun <T : Activity> View.activity() = context as T

internal fun View.onCreateOptionsMenu(onCreateOptionsMenu: (Menu) -> Unit) {
    activity<NavigatingActivity>().onCreateOptionsMenu = onCreateOptionsMenu
    activity.invalidateOptionsMenu()
}

internal fun View.goTo(screen: Screen) {
    activity<NavigatingActivity>().goTo(screen)
}

internal fun View.goBack() {
    activity<NavigatingActivity>().goBack()
}

internal fun Context.getColorCompat(id: Int): Int {
    return if (VERSION.SDK_INT >= 23) {
        getColor(id)
    } else {
        resources.getColor(id)
    }
}

//将响应放tag上
internal fun View.onScreenExiting(block: () -> Unit) {
    @Suppress("UNCHECKED_CAST")
    //callbacks是一个list
    var callbacks = getTag(R.id.leak_canary_notification_on_screen_exit) as MutableList<() -> Unit>?
    if (callbacks == null) {
        callbacks = mutableListOf()
        setTag(R.id.leak_canary_notification_on_screen_exit, callbacks)
    }
    callbacks.add(block)
}

internal fun View.notifyScreenExiting() {
    @Suppress("UNCHECKED_CAST")
    val callbacks = getTag(R.id.leak_canary_notification_on_screen_exit)
            as MutableList<() -> Unit>?
    //挨个callback调用invoke
    callbacks?.forEach { it.invoke() }
}

8.29 简单的NavigatingActivity导航activity

/**
 * A simple backstack navigating activity
 */
internal abstract class NavigatingActivity : Activity() {

    private lateinit var backstack: ArrayList<BackstackFrame> //view层级关系
    private lateinit var currentScreen: Screen                //现在的view

    private lateinit var container: ViewGroup                 //视图容器
    private lateinit var currentView: View                    //现在的view

    var onCreateOptionsMenu = NO_MENU //menu

    fun installNavigation(
            savedInstanceState: Bundle?,
            container: ViewGroup
    ) {
        this.container = container

        if (savedInstanceState == null) {
            backstack = ArrayList()
            currentScreen = if (intent.hasExtra("screens")) {
                @Suppress("UNCHECKED_CAST")
                val screens = intent.getSerializableExtra("screens") as List<Screen>
                screens.dropLast(1)//去掉最后1个开始取值 https://blog.csdn.net/sinat_31057219/article/details/105996133
                        .forEach { screen ->
                            backstack.add(BackstackFrame(screen))//放到backstack里面
                        }
                screens.last()//最后一个
            } else {
                getLauncherScreen()
            }
        } else {
            currentScreen = savedInstanceState.getSerializable("currentScreen") as Screen
            @Suppress("UNCHECKED_CAST")
            backstack = savedInstanceState.getParcelableArrayList<Parcelable>(
                    "backstack"
            ) as ArrayList<BackstackFrame>
        }
        currentView = currentScreen.createView(container)
        container.addView(currentView)

        actionBar?.run {
            setHomeButtonEnabled(true)//决定左上角的图标是否可以点击。没有向左的小图标。 true 图标可以点击  false 不可以点击。
            setDisplayHomeAsUpEnabled(true)// 给左上角图标的左边加上一个返回的图标
        }
        screenUpdated()
    }

    override fun onNewIntent(intent: Intent) {
        if (intent.hasExtra("screens")) {
            @Suppress("UNCHECKED_CAST")
            val screens = intent.getSerializableExtra("screens") as List<Screen> //堆栈里放其他页面
            goTo(intent.getSerializableExtra("screen") as Screen)   //去screen页面
            backstack.clear()
            screens.dropLast(1)///去掉最后1个开始取值,放堆栈里
                    .forEach { screen ->
                        backstack.add(BackstackFrame(screen))
                    }
        }
    }

    //子类可以覆盖这个方法
    open fun getLauncherScreen(): Screen {
        TODO("Launcher activities should override getLauncherScreen()")
    }

    //保存状态
    public override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putSerializable("currentScreen", currentScreen)
        outState.putParcelableArrayList("backstack", backstack)
    }

    //堆栈里还有,就goback,没有super.onBackPressed()
    override fun onBackPressed() {
        if (backstack.size > 0) {
            goBack()
            return
        }
        super.onBackPressed()
    }

    fun resetTo(screen: Screen) {
        onCreateOptionsMenu = NO_MENU

        currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_exit_alpha))//currentView动画
        container.removeView(currentView)//container 移除当前view
        currentView.notifyScreenExiting()//currentView 通知notifyScreenExiting

        backstack.clear()//清空

        currentScreen = screen //当前screen重置
        currentView = currentScreen.createView(container)//创建当前view
        currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_enter_alpha))//当前view开启动画
        container.addView(currentView)//添加view

        screenUpdated()//刷新menu,添加goback按钮,调用onNewScreen(子类可以覆盖)
    }

    fun goTo(screen: Screen) {
        onCreateOptionsMenu = NO_MENU

        currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_exit_forward))
        container.removeView(currentView)//去掉当前view
        currentView.notifyScreenExiting()//通知
        //- 当状态需要保存的时候被安卓framework调用,通常会调用dispatchSaveInstanceState() 。
        val backstackFrame = BackstackFrame(currentScreen, currentView)//这里会调用saveHierarchyState
        backstack.add(backstackFrame)

        currentScreen = screen
        currentView = currentScreen.createView(container)
        currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_enter_forward))
        container.addView(currentView)

        screenUpdated()//刷新menu,添加goback按钮,调用onNewScreen(子类可以覆盖)
    }

    fun refreshCurrentScreen() {//当前view刷新
        onCreateOptionsMenu = NO_MENU
        container.removeView(currentView)
        currentView.notifyScreenExiting()
        currentView = currentScreen.createView(container)
        container.addView(currentView)

        screenUpdated()//刷新menu,添加goback按钮,调用onNewScreen(子类可以覆盖)
    }

    fun goBack() {
        onCreateOptionsMenu = NO_MENU

        currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_exit_backward))
        container.removeView(currentView)
        currentView.notifyScreenExiting()

        val latest = backstack.removeAt(backstack.size - 1)
        currentScreen = latest.screen
        currentView = currentScreen.createView(container)
        currentView.startAnimation(loadAnimation(this, R.anim.leak_canary_enter_backward))
        container.addView(currentView, 0)
        latest.restore(currentView)//会调用restoreHierarchyState

        screenUpdated()//刷新menu,添加goback按钮,调用onNewScreen(子类可以覆盖)
    }

    private fun screenUpdated() {
        invalidateOptionsMenu()//刷新menu
        if (SDK_INT >= 18) {
            actionBar?.run {//添加goback按钮
                val goBack = backstack.size > 0
                val indicator = if (goBack) 0 else android.R.drawable.ic_menu_close_clear_cancel
                setHomeAsUpIndicator(indicator)
            }
        }
        onNewScreen(currentScreen)//子类可以覆盖这个方法
    }

    //而在kotlin的世界里面则不是这样,在kotlin中它所有的类默认都是final的,那么就意味着不能被继承,
    // 而且在类中所有的方法也是默认是final的,那么就是kotlin的方法默认也不能被重写。那么想在kotlin中继承父类应该怎么做呢?
    protected open fun onNewScreen(screen: Screen) {//
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        onCreateOptionsMenu.invoke(menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean =
            when (item.itemId) {
              android.R.id.home -> {
                onBackPressed()
                true
              }
                else -> super.onOptionsItemSelected(item)
            }

    override fun onDestroy() {
        super.onDestroy()
        currentView.notifyScreenExiting()
    }

    companion object {
        //伴随对象,静态方法,静态值
        val NO_MENU: ((Menu) -> Unit) = {}
    }
}

8.30 SimpleListAdapter 简化的adapter,就不用单独实现一个类了

//SimpleListAdapter bindView为扩展方法
internal class SimpleListAdapter<T>(
  private val rowResId: Int,
  private val items: List<T>,
  private val bindView: SimpleListAdapter<T>.(View, Int) -> Unit
) : BaseAdapter() {

  override fun getView(
    position: Int,
    convertView: View?,
    parent: ViewGroup
  ): View {
    val view = convertView ?: parent.inflate(rowResId)
    bindView(view, position)
    return view
  }

  override fun getItem(position: Int) = items[position]

  override fun getItemId(position: Int) = position.toLong()

  override fun getCount() = items.size
}

8.31 TimeFormatter时间格式化

//时间格式化
internal object TimeFormatter {

  private const val MINUTE_MILLIS = 60 * 1000
  private const val TWO_MINUTES_MILLIS = 2 * MINUTE_MILLIS
  private const val FIFTY_MINUTES_MILLIS = 50 * MINUTE_MILLIS
  private const val NINETY_MINUTES_MILLIS = 90 * MINUTE_MILLIS
  private const val HOUR_MILLIS = 60 * MINUTE_MILLIS
  private const val DAY_MILLIS = 24 * HOUR_MILLIS
  private const val TWO_DAYS_MILLIS = 48 * HOUR_MILLIS

  fun formatTimestamp(
    context: Context,
    timestampMillis: Long
  ): String {
    // Based on https://stackoverflow.com/a/13018647
    val nowMillis = System.currentTimeMillis()
    return when (val diff = nowMillis - timestampMillis) {
      in 0..MINUTE_MILLIS -> {
        "just now"
      }
      in MINUTE_MILLIS..TWO_MINUTES_MILLIS -> {
        "a minute ago"
      }
      in TWO_MINUTES_MILLIS..FIFTY_MINUTES_MILLIS -> {
        "${diff / MINUTE_MILLIS} minutes ago"
      }
      in FIFTY_MINUTES_MILLIS..NINETY_MINUTES_MILLIS -> {
        "an hour ago"
      }
      in NINETY_MINUTES_MILLIS..DAY_MILLIS -> {
        "${diff / HOUR_MILLIS} hours ago"
      }
      in DAY_MILLIS..TWO_DAYS_MILLIS -> {
        "yesterday"
      }
      else -> {
        DateUtils.formatDateTime(
          context, timestampMillis,
          DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE
        )
      }
    }
  }
}

8.32 UiUtils UrlSpan改为事件响应,urlAction是一个通过url来决定action的lambda函数

internal object UiUtils {
  //UrlSpan改为事件响应,urlAction是一个通过url来决定action的lambda函数
  internal fun replaceUrlSpanWithAction(
    title: SpannableStringBuilder,
    urlAction: (String) -> (() -> Unit)?
  ) {
    val urlSpans = title.getSpans(0, title.length, URLSpan::class.java)
    for (span in urlSpans) {
      val action: (() -> Unit)? = urlAction(span.url)
      if (action != null) {
        val start = title.getSpanStart(span)
        val end = title.getSpanEnd(span)
        val flags = title.getSpanFlags(span)
        title.removeSpan(span)
        val newSpan = object : ClickableSpan() {
          override fun onClick(widget: View) {
            action()
          }
        }
        title.setSpan(newSpan, start, end, flags)
      }
    }
  }
}

8.33 引用链接item相关的3个类

//引用链 左侧小箭头 setType
internal class DisplayLeakConnectorView(
        context: Context,
        attrs: AttributeSet
)
/ * 红色波浪形下划线
 *
 * replaceUnderlineSpans 将UnderlineSpan换为SquigglySpan
 *
 *  Squiggly adj.  (线条)不规则的,波形的;
 */
internal class SquigglySpan(context: Context) : ReplacementSpan() 
//引用链接item
internal class RowElementLayout(
        context: Context,
        attrs: AttributeSet
)

8.34 LeakActivity主页,主要是管理tab页,和处理手动选择dump文件importHprof方法

internal class LeakActivity : NavigatingActivity() {

    private val leaksButton by lazy {
        findViewById<View>(R.id.leak_canary_navigation_button_leaks)
    }

    private val leaksButtonIconView by lazy {
        findViewById<View>(R.id.leak_canary_navigation_button_leaks_icon)
    }

    private val heapDumpsButton by lazy {
        findViewById<View>(R.id.leak_canary_navigation_button_heap_dumps)
    }

    private val heapDumpsButtonIconView by lazy {
        findViewById<View>(R.id.leak_canary_navigation_button_heap_dumps_icon)
    }

    private val aboutButton by lazy {
        findViewById<View>(R.id.leak_canary_navigation_button_about)
    }

    private val aboutButtonIconView by lazy {
        findViewById<View>(R.id.leak_canary_navigation_button_about_icon)
    }

    private val bottomNavigationBar by lazy {
        findViewById<View>(R.id.leak_canary_bottom_navigation_bar)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.leak_canary_leak_activity)
        //添加相应页面,根据intent/getLauncherScreen/savedInstanceState
        installNavigation(savedInstanceState, findViewById(R.id.leak_canary_main_container))

        leaksButton.setOnClickListener { resetTo(LeaksScreen()) }
        heapDumpsButton.setOnClickListener { resetTo(HeapDumpsScreen()) }
        aboutButton.setOnClickListener { resetTo(AboutScreen()) }

        handleViewHprof(intent)
    }

    //处理跳转查看hprof文件的逻辑
    private fun handleViewHprof(intent: Intent?) {
        if (intent?.action != Intent.ACTION_VIEW) return
        val uri = intent.data ?: return
        if (uri.lastPathSegment?.endsWith(".hprof") != true) {
            //Could not import %s, this is not an hprof file.
            Toast.makeText(this, getString(R.string.leak_canary_import_unsupported_file_extension, uri.lastPathSegment), Toast.LENGTH_LONG).show()
            return
        }
        resetTo(HeapDumpsScreen())//HeapDumpsScreen页面
        AsyncTask.THREAD_POOL_EXECUTOR.execute {
            importHprof(uri)//用线程加载hprof
        }
    }

    override fun onNewScreen(screen: Screen) {
        when (screen) {
          is LeaksScreen -> {//第一页
            bottomNavigationBar.visibility = View.VISIBLE
            leaksButton.isSelected = true
            leaksButtonIconView.alpha = 1.0f
            heapDumpsButton.isSelected = false
            heapDumpsButtonIconView.alpha = 0.4f
            aboutButton.isSelected = false
            aboutButtonIconView.alpha = 0.4f
          }
          is HeapDumpsScreen -> {//中间页
            bottomNavigationBar.visibility = View.VISIBLE
            leaksButton.isSelected = false
            leaksButtonIconView.alpha = 0.4f
            heapDumpsButton.isSelected = true
            heapDumpsButtonIconView.alpha = 1.0f
            aboutButton.isSelected = false
            aboutButtonIconView.alpha = 0.4f
          }
          is AboutScreen -> {//第三页
            bottomNavigationBar.visibility = View.VISIBLE
            leaksButton.isSelected = false
            leaksButtonIconView.alpha = 0.4f
            heapDumpsButton.isSelected = false
            heapDumpsButtonIconView.alpha = 0.4f
            aboutButton.isSelected = true
            aboutButtonIconView.alpha = 1.0f
          }
            else -> {
                bottomNavigationBar.visibility = View.GONE
            }
        }
    }

    override fun getLauncherScreen(): Screen {
        return LeaksScreen()//默认页面
    }

    fun requestImportHprof() {
        val requestFileIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
            type = "*/*"
            addCategory(Intent.CATEGORY_OPENABLE)
        }
        //Import From…
        val chooserIntent = Intent.createChooser(
                requestFileIntent, resources.getString(R.string.leak_canary_import_from_title)
        )
        startActivityForResult(chooserIntent, FILE_REQUEST_CODE)
    }

    override fun onActivityResult(
            requestCode: Int,
            resultCode: Int,
            returnIntent: Intent?
    ) {
        SharkLog.d {
            "Got activity result with requestCode=$requestCode resultCode=$resultCode returnIntent=$returnIntent"
        }
        if (requestCode == FILE_REQUEST_CODE && resultCode == RESULT_OK && returnIntent != null) {
            returnIntent.data?.let { fileUri ->
                AsyncTask.THREAD_POOL_EXECUTOR.execute {
                    importHprof(fileUri)//加载文件
                }
            }
        }
    }

    private fun importHprof(fileUri: Uri) {
        try {
            contentResolver.openFileDescriptor(fileUri, "r")
                    ?.fileDescriptor?.let { fileDescriptor ->
                        val inputStream = FileInputStream(fileDescriptor)
                        InternalLeakCanary.createLeakDirectoryProvider(this)
                                .newHeapDumpFile()
                                ?.let { target ->
                                    inputStream.use { input ->
                                        target.outputStream()
                                                .use { output ->
                                                    //将文件copy到我们的目录里
                                                    input.copyTo(output, DEFAULT_BUFFER_SIZE)//input FileInputStream
                                                }
                                    }
                                    //使用HeapAnalyzerService进行分析
                                    HeapAnalyzerService.runAnalysis(this, target, heapDumpReason = "Imported by user")
                                }
                    }
        } catch (e: IOException) {
            SharkLog.d(e) { "Could not imported Hprof file" }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (!isChangingConfigurations) {
            Db.closeDatabase()//关闭db
        }
    }

    override fun setTheme(resid: Int) {
        // We don't want this to be called with an incompatible theme.
        // This could happen if you implement runtime switching of themes
        // using ActivityLifecycleCallbacks.
        if (resid != R.style.leak_canary_LeakCanary_Base) {
            return
        }
        super.setTheme(resid)
    }

    companion object {
        private const val FILE_REQUEST_CODE = 0

        fun createPendingIntent(
                context: Context,
                screens: ArrayList<Screen>
        ): PendingIntent {//在notification里用
            val intent = Intent(context, LeakActivity::class.java)
            intent.putExtra("screens", screens)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
            val flags = if (Build.VERSION.SDK_INT >= 23) {
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            } else {
                PendingIntent.FLAG_UPDATE_CURRENT
            }
            return PendingIntent.getActivity(context, 1, intent, flags)
        }

        fun createIntent(context: Context): Intent {
            val intent = Intent(context, LeakActivity::class.java)
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
            return intent
        }
    }
}

8.35 Cursors.kt 扩展函数

//扩展函数,Cursor.use,SQLiteDatabase.inTransaction
//Cursor用完关闭
internal inline fun <R> Cursor.use(block: (Cursor) -> R): R {
  var exception: Throwable? = null
  try {
    return block(this)
  } catch (e: Throwable) {
    exception = e
    throw e
  } finally {
    when (exception) {
      null -> close()
      else -> try {
        close()
      } catch (ignoredCloseException: Throwable) {
      }
    }
  }
}

//ThreadLocal每个线程单独的变量
private val inTransaction = ThreadLocal<Boolean>()

//SQLiteDatabase.inTransaction每个线程的事物处理inTransaction 交易;处理;业务;买卖;办理
internal inline fun <T> SQLiteDatabase.inTransaction(block: SQLiteDatabase.() -> T): T {
  if (inTransaction.getOrSet { false }) {
    return block()
  }
  try {
    inTransaction.set(true)
    beginTransaction()
    val result = block()
    setTransactionSuccessful()
    return result
  } finally {
    endTransaction()
    inTransaction.set(false)
  }
}

8.36 LeakTraceTable 引用连其中的一个

//LeakTraceTable表结构
internal object LeakTraceTable {

  //leak_trace_index 引用连的位置,第几个
  @Language("RoomSql")
  const val create = """
        CREATE TABLE leak_trace
        (
        id INTEGER PRIMARY KEY,
        heap_analysis_id REFERENCES heap_analysis(id),
        leak_id REFERENCES leak(id),
        class_simple_name TEXT,
        leak_trace_index INTEGER
        )"""

  @Language("RoomSql")
  const val drop = "DROP TABLE IF EXISTS leak_trace"

  fun insert(
    db: SQLiteDatabase,
    leakId: Long,
    heapAnalysisId: Long,
    leakTraceIndex: Int,
    leakingObjectClassSimpleName: String
  ): Long {
    val values = ContentValues()
    values.put("heap_analysis_id", heapAnalysisId)
    values.put("leak_id", leakId)
    values.put("class_simple_name", leakingObjectClassSimpleName)
    values.put("leak_trace_index", leakTraceIndex)//引用连的位置,第几个
    return db.insertOrThrow("leak_trace", null, values)
  }

  fun deleteByHeapAnalysisId(
    db: SQLiteDatabase,
    heapAnalysisId: Long
  ) {
    db.delete("leak_trace", "heap_analysis_id=$heapAnalysisId", null)
  }
}

8.37 Io View扩展函数,执行在io线程

package leakcanary.internal.activity.db

import android.view.View
import leakcanary.internal.activity.db.Io.OnIo
import leakcanary.internal.friendly.checkMainThread
import leakcanary.internal.friendly.mainHandler
import leakcanary.internal.navigation.onScreenExiting
import java.util.concurrent.Executors

internal object Io {

    private val serialExecutor = Executors.newSingleThreadExecutor()

    interface OnIo {
        fun updateUi(updateUi: View.() -> Unit)
    }

    private class IoContext : OnIo {
        var updateUi: (View.() -> Unit)? = null

        override fun updateUi(updateUi: View.() -> Unit) {
            this.updateUi = updateUi
        }
    }

    fun execute(block: () -> Unit) {
        serialExecutor.execute(block)
    }

    //第二步
    fun execute(
            view: View,
            block: OnIo.() -> Unit
    ) {
        checkMainThread()
        //原子封装对象
        val viewWrapper: VolatileObjectRef<View> = VolatileObjectRef(view)
        view.onScreenExiting {//当view不见得时候,设置element
            viewWrapper.element = null
        }
        serialExecutor.execute backgroundExecute@{
            if (viewWrapper.element == null) {
                return@backgroundExecute
            }
            val context = IoContext()
            //block里会处理后台事物,并且对context的updateUi赋值
            block(context)
            //block里设置了updateUi方法,如果有,则执行
            val updateUi = context.updateUi
            if (viewWrapper.element != null && updateUi != null) {
                mainHandler.post mainThreadPost@{
                    val attachedView = viewWrapper.element ?: return@mainThreadPost
                    //刷新ui
                    updateUi(attachedView)
                }
            }
        }
    }

    /**
     * Similar to kotlin.jvm.internal.Ref.ObjectRef but volatile
     * 封装原子性对象
     */
    private class VolatileObjectRef<T>(
            @Volatile
            var element: T? = null
    )
}

//第一步
//View扩展函数,执行在io线程
internal fun View.executeOnIo(block: OnIo.() -> Unit) {
    Io.execute(this, block)
}

8.38 Db View扩展函数,执行在io线程

internal object Db {

    // Accessed on the IO thread only.
    private var dbHelper: LeaksDbHelper? = null

    interface OnDb : OnIo {
        val db: SQLiteDatabase
    }

    private class DbContext(override val db: SQLiteDatabase) : OnDb {
        var updateUi: (View.() -> Unit)? = null

        override fun updateUi(updateUi: View.() -> Unit) {
            this.updateUi = updateUi
        }
    }

    //第二步,执行方法块
    fun execute(
            view: View,
            block: OnDb.() -> Unit
    ) {
        val appContext = view.context.applicationContext
        Io.execute(view) {
            //创建dbHelper
            if (dbHelper == null) {
                dbHelper = LeaksDbHelper(appContext)
            }
            //获取DbContext,使用dbHelper的writableDatabase
            val dbBlock = DbContext(dbHelper!!.writableDatabase)
            block(dbBlock)//执行block方法
            //block里设置了updateUi方法,如果有,则执行
            val updateUi = dbBlock.updateUi
            if (updateUi != null) {
                updateUi(updateUi)
            }
        }
    }

    fun closeDatabase() {
        // Closing on the serial IO thread to ensure we don't close while using the db.
        Io.execute {
            dbHelper?.close()
        }
    }
}

//第一步,扩展函数
internal fun View.executeOnDb(block: OnDb.() -> Unit) {
    Db.execute(this, block)
}

8.39 根据signature来区分的泄漏

internal object LeakTable {

    //signature 特征是唯一
    // short_description 简单描述
    // is_library_leak 是否是library泄漏
    // is_read 是否read
    @Language("RoomSql")
    const val create = """
        CREATE TABLE leak
        (
        id INTEGER PRIMARY KEY,
        signature TEXT UNIQUE,
        short_description TEXT,
        is_library_leak INTEGER,
        is_read INTEGER
        )"""

    //对signature建立一个索引
    @Language("RoomSql")
    const val createSignatureIndex = """
        CREATE INDEX leak_signature
        on leak (signature)
    """

    //删除表
    @Language("RoomSql")
    const val drop = "DROP TABLE IF EXISTS leak"

    //插入
    fun insert(
            db: SQLiteDatabase,
            heapAnalysisId: Long,
            leak: Leak
    ): Long {
        //插入leak表
        val values = ContentValues()
        values.put("signature", leak.signature)
        values.put("short_description", leak.shortDescription)
        values.put("is_library_leak", if (leak is LibraryLeak) 1 else 0)
        values.put("is_read", 0)

        db.insertWithOnConflict("leak", null, values, SQLiteDatabase.CONFLICT_IGNORE)

        val leakId =
                db.rawQuery("SELECT id from leak WHERE signature = '${leak.signature}' LIMIT 1", null)
                        .use { cursor ->
                            if (cursor.moveToFirst()) cursor.getLong(0) else throw IllegalStateException(
                                    "No id found for leak with signature '${leak.signature}'"
                            )
                        }

        //插入leak_trace表
        leak.leakTraces.forEachIndexed { index, leakTrace ->
            LeakTraceTable.insert(
                    db = db,
                    leakId = leakId,
                    heapAnalysisId = heapAnalysisId,
                    leakTraceIndex = index,//位置,引用连的第几个第几个
                    leakingObjectClassSimpleName = leakTrace.leakingObject.classSimpleName//名字
            )
        }

        return leakId
    }

    //根据signatures获取read
    fun retrieveLeakReadStatuses(
            db: SQLiteDatabase,
            signatures: Set<String>
    ): Map<String, Boolean> {
        return db.rawQuery(
                """
      SELECT
      signature
      , is_read
      FROM leak
      WHERE signature IN (${signatures.joinToString { "'$it'" }})
    """, null
        )
                .use { cursor ->
                    val leakReadStatuses = mutableMapOf<String, Boolean>()
                    while (cursor.moveToNext()) {
                        val signature = cursor.getString(0)//signature
                        val isRead = cursor.getInt(1) == 1//read标记
                        leakReadStatuses[signature] = isRead
                    }
                    leakReadStatuses
                }
    }

    //全部的leak 分类
    class AllLeaksProjection(
            val signature: String,
            val shortDescription: String,
            val createdAtTimeMillis: Long,
            val leakTraceCount: Int,
            val isLibraryLeak: Boolean,
            val isNew: Boolean
    )

    //获取全部的leak,按l.signature分组
    //GROUP BY 1它的意思是按第一列分组,而不管它的名称是什么
    fun retrieveAllLeaks(
            db: SQLiteDatabase
    ): List<AllLeaksProjection> {
        return db.rawQuery(
                """
          SELECT
          l.signature
          , MIN(l.short_description)
          , MAX(h.created_at_time_millis) as created_at_time_millis
          , COUNT(*) as leak_trace_count
          , MIN(l.is_library_leak) as is_library_leak
          , MAX(l.is_read) as is_read
          FROM leak_trace lt
          LEFT JOIN leak l on lt.leak_id = l.id
          LEFT JOIN heap_analysis h ON lt.heap_analysis_id = h.id
          GROUP BY 1
          ORDER BY leak_trace_count DESC, created_at_time_millis DESC
          """, null
        )
                .use { cursor ->
                    val all = mutableListOf<AllLeaksProjection>()
                    while (cursor.moveToNext()) {
                        val group = AllLeaksProjection(
                                signature = cursor.getString(0),
                                shortDescription = cursor.getString(1),
                                createdAtTimeMillis = cursor.getLong(2),
                                leakTraceCount = cursor.getInt(3),
                                isLibraryLeak = cursor.getInt(4) == 1,//是否是library泄漏
                                isNew = cursor.getInt(5) == 0//是否new
                        )
                        all.add(group)
                    }
                    all
                }
    }

    //signature是唯一的
    fun markAsRead(
            db: SQLiteDatabase,
            signature: String
    ) {
        val values = ContentValues().apply { put("is_read", 1) }
        db.update("leak", values, "signature = ?", arrayOf(signature))
    }

    class LeakProjection(
            val shortDescription: String,//描述
            val isNew: Boolean,//是否新的
            val isLibraryLeak: Boolean,//library
            val leakTraces: List<LeakTraceProjection> //引用连
    )

    class LeakTraceProjection(
            val leakTraceIndex: Int,//第几个位置
            val heapAnalysisId: Long,//heap id
            val classSimpleName: String,//类名
            val createdAtTimeMillis: Long//创建时间
    )

    //获取LeakProjection,预测; 推断; 设想; 投射; 放映; 投影; 放映的影像; 投影图;
    //获取一个signature的所有泄漏
    fun retrieveLeakBySignature(
            db: SQLiteDatabase,
            signature: String
    ): LeakProjection? {
        return db.rawQuery(
                """
          SELECT
          lt.leak_trace_index
          , lt.heap_analysis_id
          , lt.class_simple_name
          , h.created_at_time_millis
          , l.short_description
          , l.is_read
          , l.is_library_leak
          FROM leak_trace lt
          LEFT JOIN leak l on lt.leak_id = l.id
          LEFT JOIN heap_analysis h ON lt.heap_analysis_id = h.id
          WHERE l.signature = ?
          ORDER BY h.created_at_time_millis DESC
          """, arrayOf(signature)
        )
                .use { cursor ->
                    return if (cursor.moveToFirst()) {
                        val leakTraces = mutableListOf<LeakTraceProjection>()
                        val leakProjection = LeakProjection(
                                shortDescription = cursor.getString(4),
                                isNew = cursor.getInt(5) == 0,
                                isLibraryLeak = cursor.getInt(6) == 1,
                                leakTraces = leakTraces
                        )
                        leakTraces.addAll(generateSequence(cursor, {
                            if (cursor.moveToNext()) cursor else null
                        }).map {
                            LeakTraceProjection(
                                    leakTraceIndex = cursor.getInt(0),
                                    heapAnalysisId = cursor.getLong(1),
                                    classSimpleName = cursor.getString(2),
                                    createdAtTimeMillis = cursor.getLong(3)
                            )
                        })
                        leakProjection
                    } else {
                        null
                    }
                }
    }

    //删除leak,首先在leak_trace表删除,然后在leak表删除
    fun deleteByHeapAnalysisId(
            db: SQLiteDatabase,
            heapAnalysisId: Long
    ) {
        LeakTraceTable.deleteByHeapAnalysisId(db, heapAnalysisId)
        db.execSQL(
                """
      DELETE
      FROM leak
      WHERE NOT EXISTS (
      SELECT *
      FROM leak_trace lt
      WHERE leak.id = lt.leak_id)
    """
        )
    }
}

8.40 HeapAnalysisTable一个heap计算出来的泄漏信息

//一个heap计算出来的泄漏信息
internal object HeapAnalysisTable {

    /**
     * CopyOnWriteArrayList because registered listeners can remove themselves from this list while
     * iterating and invoking them, which would trigger a ConcurrentModificationException (see #2019).
     */
    private val updateListeners = CopyOnWriteArrayList<() -> Unit>()

    //创建时间,执行时间,泄漏个数,exception总结,object大对象
    @Language("RoomSql")
    const val create = """CREATE TABLE heap_analysis
        (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        created_at_time_millis INTEGER,
        dump_duration_millis INTEGER DEFAULT -1,
        leak_count INTEGER DEFAULT 0,
        exception_summary TEXT DEFAULT NULL,
        object BLOB
        )"""

    //删除
    @Language("RoomSql")
    const val drop = "DROP TABLE IF EXISTS heap_analysis"

    //todo 这里是啥操作,这里是把block添加到updateListeners,并返回了一个函数块,块的作用是remove
    fun onUpdate(block: () -> Unit): () -> Unit {
        updateListeners.add(block)
        return {
            updateListeners.remove(block)
        }
    }

    fun insert(
            db: SQLiteDatabase,
            heapAnalysis: HeapAnalysis
    ): Long {
        val values = ContentValues()
        values.put("created_at_time_millis", heapAnalysis.createdAtTimeMillis)
        values.put("dump_duration_millis", heapAnalysis.dumpDurationMillis)
        values.put("object", heapAnalysis.toByteArray())
        when (heapAnalysis) {
            is HeapAnalysisSuccess -> {
                val leakCount = heapAnalysis.applicationLeaks.size + heapAnalysis.libraryLeaks.size
                values.put("leak_count", leakCount)
            }
            is HeapAnalysisFailure -> {
                val cause = heapAnalysis.exception.cause!!
                val exceptionSummary = "${cause.javaClass.simpleName} ${cause.message}"
                values.put("exception_summary", exceptionSummary)
            }
        }

        return db.inTransaction {
            //插入heap_analysis表
            val heapAnalysisId = db.insertOrThrow("heap_analysis", null, values)
            if (heapAnalysis is HeapAnalysisSuccess) {
                heapAnalysis.allLeaks
                        .forEach { leakingInstance ->
                            //插入leak表,然后插入leak_trace表
                            LeakTable.insert(
                                    db, heapAnalysisId, leakingInstance
                            )
                        }
            }
            heapAnalysisId
        }.apply { notifyUpdateOnMainThread() }
    }

    private fun notifyUpdateOnMainThread() {
        checkNotMainThread()
        mainHandler.post {
            updateListeners.forEach { it() }
        }
    }

    //删除某个heap_analysis
    inline fun <reified T : HeapAnalysis> retrieve(
            db: SQLiteDatabase,
            id: Long
    ): T? {
        return db.rawQuery(
                """
              SELECT
              object
              FROM heap_analysis
              WHERE id=$id
              """, null
        )
                .use { cursor ->
                    if (cursor.moveToNext()) {
                        val analysis = Serializables.fromByteArray<T>(cursor.getBlob(0))
                        if (analysis == null) {
                            delete(db, id, null)
                        }
                        analysis
                    } else {
                        null
                    }
                }
    }

    fun retrieveAll(db: SQLiteDatabase): List<Projection> {
        return db.rawQuery(
                """
          SELECT
          id
          , created_at_time_millis
          , leak_count
          , exception_summary
          FROM heap_analysis
          ORDER BY created_at_time_millis DESC
          """, null
        )
                .use { cursor ->
                    val all = mutableListOf<Projection>()
                    while (cursor.moveToNext()) {
                        val summary = Projection(
                                id = cursor.getLong(0),
                                createdAtTimeMillis = cursor.getLong(1),
                                leakCount = cursor.getInt(2),
                                exceptionSummary = cursor.getString(3)
                        )
                        all.add(summary)
                    }
                    all
                }
    }

    fun delete(
            db: SQLiteDatabase,
            heapAnalysisId: Long,
            heapDumpFile: File?
    ) {
        if (heapDumpFile != null) {
            AsyncTask.SERIAL_EXECUTOR.execute {
                val path = heapDumpFile.absolutePath
                val heapDumpDeleted = heapDumpFile.delete()
                if (heapDumpDeleted) {
                    LeakDirectoryProvider.filesDeletedRemoveLeak += path
                } else {
                    SharkLog.d { "Could not delete heap dump file ${heapDumpFile.path}" }
                }
            }
        }

        db.inTransaction {
            db.delete("heap_analysis", "id=$heapAnalysisId", null)
            LeakTable.deleteByHeapAnalysisId(db, heapAnalysisId)
        }
        notifyUpdateOnMainThread()
    }

    fun deleteAll(db: SQLiteDatabase) {
        db.inTransaction {
            rawQuery(
                    """
              SELECT
              id,
              object
              FROM heap_analysis
              """, null
            )
                    .use { cursor ->
                        val all = mutableListOf<Pair<Long, HeapAnalysis>>()
                        while (cursor.moveToNext()) {
                            val id = cursor.getLong(0)
                            val analysis = Serializables.fromByteArray<HeapAnalysis>(cursor.getBlob(1))
                            if (analysis != null) {
                                all += id to analysis
                            }
                        }
                        all.forEach { (id, _) ->
                            db.delete("heap_analysis", "id=$id", null)
                            LeakTable.deleteByHeapAnalysisId(db, id)
                        }
                        AsyncTask.SERIAL_EXECUTOR.execute {
                            all.forEach { (_, analysis) ->
                                analysis.heapDumpFile.delete()
                            }
                        }
                    }
        }
        notifyUpdateOnMainThread()
    }

    class Projection(
            val id: Long,
            val createdAtTimeMillis: Long,
            val leakCount: Int,
            val exceptionSummary: String?
    )
}

8.41 HeapDumpsScreen中间页

//中间页
internal class HeapDumpsScreen : Screen() {
    override fun createView(container: ViewGroup) =
            container.inflate(R.layout.leak_canary_heap_dumps_screen).apply {

                val unsubscribeRefresh = HeapAnalysisTable.onUpdate {//这里有个刷新
                    activity<NavigatingActivity>().refreshCurrentScreen()
                }

                onScreenExiting { unsubscribeRefresh() }//退出的关掉数据库监听

                onCreateOptionsMenu { menu ->
                    if (!ActivityManager.isUserAMonkey()) {
                        menu.add(R.string.leak_canary_delete_all)
                                .setOnMenuItemClickListener {
                                    AlertDialog.Builder(context)
                                            .setIcon(android.R.drawable.ic_dialog_alert)
                                            .setTitle(R.string.leak_canary_delete_all)
                                            .setMessage(R.string.leak_canary_delete_all_leaks_title)
                                            .setPositiveButton(android.R.string.ok) { _, _ ->
                                                executeOnDb {
                                                    //删除
                                                    HeapAnalysisTable.deleteAll(db)
                                                    updateUi {
                                                        //设置空
                                                        val listView = findViewById<ListView>(R.id.leak_canary_list)
                                                        listView.adapter =
                                                                SimpleListAdapter(
                                                                        R.layout.leak_canary_simple_row, emptyList<Any>()
                                                                ) { _, _ -> }
                                                    }
                                                }
                                            }
                                            .setNegativeButton(android.R.string.cancel, null)
                                            .show()
                                    true
                                }
                    }
                }

                findViewById<View>(R.id.leak_canary_import_heap_dump).setOnClickListener {
                    activity<LeakActivity>().requestImportHprof()//选择dump文件
                }

                findViewById<View>(R.id.leak_canary_dump_heap_now).setOnClickListener {
                    LeakCanary.dumpHeap()//dump
                }

                executeOnDb {
                    val projections = HeapAnalysisTable.retrieveAll(db)
                    updateUi { onAnalysesRetrieved(projections) }
                }

            }

    private fun View.onAnalysesRetrieved(projections: List<Projection>) {
        activity.title = resources.getQuantityString(
                R.plurals.leak_canary_heap_analysis_list_screen_title,
                projections.size, projections.size
        )

        val listView = findViewById<ListView>(R.id.leak_canary_list)

        listView.setOnItemClickListener { _, _, position, _ ->
            val projection = projections[position]
            val analysisScreen = if (projection.exceptionSummary != null) {
                HeapAnalysisFailureScreen(projection.id)
            } else {
                HeapDumpScreen(projection.id)
            }
            goTo(analysisScreen)
        }

        listView.adapter =
                SimpleListAdapter(R.layout.leak_canary_leak_row, projections) { view, position ->
                    val goneView = view.findViewById<TextView>(R.id.leak_canary_count_text)
                    goneView.visibility = View.GONE
                    val timeView = view.findViewById<TextView>(R.id.leak_canary_leak_text)
                    val countView = view.findViewById<TextView>(R.id.leak_canary_time_text)

                    val projection = getItem(position)
                    // Enable means "new"
                    countView.isEnabled = false

                    timeView.text = TimeFormatter.formatTimestamp(view.context, projection.createdAtTimeMillis)

                    val count = projection.exceptionSummary ?: resources.getQuantityString(
                            R.plurals.leak_canary_distinct_leaks,
                            projection.leakCount, projection.leakCount
                    )
                    countView.text = count
                }
    }
}

8.42 //第一页 ok

//第一页 ok
internal class LeaksScreen : Screen() {
    override fun createView(container: ViewGroup) =
            container.inflate(R.layout.leak_canary_list).apply {
                //leak_canary_list里只有个ListView
                val unsubscribeRefresh = HeapAnalysisTable.onUpdate {//数据库变了就更改
                    activity<NavigatingActivity>().refreshCurrentScreen()
                }

                onScreenExiting { unsubscribeRefresh() }//删除数据库变了就更改

                executeOnDb {
                    val projections = LeakTable.retrieveAllLeaks(db)
                    updateUi { onGroupsRetrieved(projections) }
                }
            }

    private fun View.onGroupsRetrieved(projections: List<AllLeaksProjection>) {
        activity.title = resources.getQuantityString(
                R.plurals.leak_canary_distinct_leaks,
                projections.size, projections.size
        )

        val listView = findViewById<ListView>(R.id.leak_canary_list)

        listView.adapter =
                SimpleListAdapter(R.layout.leak_canary_leak_row, projections) { view, position ->
                    val countView = view.findViewById<TextView>(R.id.leak_canary_count_text)
                    val descriptionView = view.findViewById<TextView>(R.id.leak_canary_leak_text)
                    val timeView = view.findViewById<TextView>(R.id.leak_canary_time_text)
                    val newChipView = view.findViewById<TextView>(R.id.leak_canary_chip_new)
                    val libraryLeakChipView = view.findViewById<TextView>(R.id.leak_canary_chip_library_leak)

                    val projection = projections[position]
                    countView.isEnabled = projection.isNew

                    newChipView.visibility = if (projection.isNew) VISIBLE else GONE
                    libraryLeakChipView.visibility = if (projection.isLibraryLeak) VISIBLE else GONE

                    countView.text = projection.leakTraceCount.toString()
                    descriptionView.text = projection.shortDescription

                    val formattedDate =
                            TimeFormatter.formatTimestamp(view.context, projection.createdAtTimeMillis)
                    timeView.text =
                            resources.getString(R.string.leak_canary_group_list_time_label, formattedDate)
                }

        listView.setOnItemClickListener { _, _, position, _ ->
            goTo(LeakScreen(projections[position].signature))
        }
    }
}

8.43 AboutScreen第三页

//ok
internal class AboutScreen : Screen() {
  override fun createView(container: ViewGroup) =
    container.inflate(R.layout.leak_canary_about_screen)
      .apply {
          //About LeakCanary %s
        activity.title =
          resources.getString(R.string.leak_canary_about_title, BuildConfig.LIBRARY_VERSION)
        val aboutTextView = findViewById<TextView>(R.id.leak_canary_about_text)
        aboutTextView.movementMethod = LinkMovementMethod.getInstance()//
        val application = activity.application
        val appName = application.packageManager.getApplicationLabel(application.applicationInfo)
        val appPackageName = context.packageName

        aboutTextView.text = Html.fromHtml(
          String.format(
            resources.getString(R.string.leak_canary_about_message), appName, appPackageName
          )
        )

        val heapDumpTextView = findViewById<TextView>(R.id.leak_canary_about_heap_dump_text)
        updateHeapDumpTextView(heapDumpTextView)
        val heapDumpSwitchView =
          findViewById<Switch>(R.id.leak_canary_about_heap_dump_switch_button)
        heapDumpSwitchView.isChecked = InternalLeakCanary.dumpEnabledInAboutScreen
          //这里有个开关
        heapDumpSwitchView.setOnCheckedChangeListener { _, checked ->
          // Updating the value wouldn't normally immediately trigger a heap dump, however
          // by updating the view we also have a side effect of querying which will notify
          // the heap dumper if the value has become positive.
          InternalLeakCanary.dumpEnabledInAboutScreen = checked
          updateHeapDumpTextView(heapDumpTextView)
        }
      }

  private fun updateHeapDumpTextView(view: TextView) {
    view.text = when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) {
      is Yup -> view.resources.getString(R.string.leak_canary_heap_dump_enabled_text)
      is Nope -> view.resources.getString(
        R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
      )

    }
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值