Android-LeakCanary原理解析

一、前言(了解ReferenceQueue)

在分析LeakCanary原理之前,首先需要了解ReferenceQueue在LeakCanary的作用。
WeakReference在創(chuàng)建時(shí),如果指定一個(gè)ReferenceQueue對象,在垃圾回收檢測到被引用的對象的可達(dá)性更改后,垃圾回收器會將已注冊的引用對象添加到ReferenceQueue對象中,等待ReferenceQueue處理。但是如果當(dāng)GC過后引用對象仍然不被加入ReferenceQueue中,就可能存在內(nèi)存泄露問題。這里ReferenceQueue對象中,存的其實(shí)就是WeakReference對象,而不是WeakReference中引用的要被回收的對象。即GC過后,WeakReference引用的對象被回收了,那么WeakReference引用的對象就是null,那么該WeakReference對象就會被加入到ReferenceQueue隊(duì)列中。
所以我們可以通過監(jiān)聽 Activity.onDestroy() 回調(diào)之后,通過弱引用(WeakReference)對象、ReferenceQueue和 GC來觀測Activity引用的內(nèi)存泄露情況,如果發(fā)現(xiàn)了未被回收的Activity對象,在找到該Activity對象是否被其他對象所引用,如果被其他對象引用,就進(jìn)行 heap dump生成完整的內(nèi)存引用鏈(最短引用鏈),并通過notification等方式展示出來。

二、LeakCanary的啟動

LeakCanary2.+的啟動,與LeakCanary1.+的不同,1.+版本的啟動,需要在Application的onCreate中手動調(diào)用LeakCanary.install方法進(jìn)行啟動;而2.+版本的啟動則不需要,而是依賴ContentProvider,因?yàn)镃ontentProvider會在Application之前被加載,所以ContentProvider的onCreate方法會在Application的onCreate方法之前被調(diào)用,所以在ContentProvider的onCreate方法中完成初始化工作。
在源碼中l(wèi)eakcanary-leaksentry中有一個(gè)LeakSentryInstaller,LeakSentryInstaller其實(shí)就是ContentProvider的一個(gè)子類,在其onCreate方法中就會調(diào)用InternalLeakSentry.install(application)進(jìn)行初始化工作。

internal class LeakSentryInstaller : ContentProvider() {

  override fun onCreate(): Boolean {
    CanaryLog.logger = DefaultCanaryLog()
    val application = context!!.applicationContext as Application
    InternalLeakSentry.install(application) // 進(jìn)行初始化工作,核心
    return true
  }
  ...
}

然后在AndroidManifest.xml中注冊該ContentProvider。在這里注冊,那么打包項(xiàng)目時(shí),會將每個(gè)庫和library中的AndroidManifest.xml合并到最終的app的androidManifest中。

<application>
  <provider
      android:name="leakcanary.internal.LeakSentryInstaller"
      android:authorities="${applicationId}.leak-sentry-installer"
      android:exported="false"/>
</application>

三、LeakCanary的初始化

LeakCanary的初始化是在InternalLeakSentry的install方法,即在ContentProvider的onCreate中調(diào)用。

1.InternalLeakSentry#install

  private val mainHandler = Handler(Looper.getMainLooper())

  init {//構(gòu)造函數(shù)
    listener = try {//InternalLeakCanary是繼承自LeakSentryListener,然后這里它是一個(gè)kotlin單例模式
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE").get(null) as LeakSentryListener
    } catch (ignored: Throwable) {
      LeakSentryListener.None
    }
  }

  private val checkRetainedExecutor = Executor { // 默認(rèn)五秒后執(zhí)行
    mainHandler.postDelayed(it, LeakSentry.config.watchDurationMillis)
  }
  val refWatcher = RefWatcher(
      clock = clock,
      checkRetainedExecutor = checkRetainedExecutor,
      onReferenceRetained = { listener.onReferenceRetained() },
      isEnabled = { LeakSentry.config.enabled }
  )
fun install(application: Application) {
  CanaryLog.d("Installing LeakSentry")
  checkMainThread() // 只能在主線程調(diào)用,否則會拋出異常
  if (this::application.isInitialized) {
    return
  }
  InternalLeakSentry.application = application

  val configProvider = { LeakSentry.config }
  // 這里監(jiān)聽頁面的銷毀
  // 在這里會調(diào)用RefWatcher.watch()方法監(jiān)測Activity的引用
  // 其中RefWatcher在InternalLeakSentry類中創(chuàng)建。
  ActivityDestroyWatcher.install( // 監(jiān)聽 Activity.onDestroy()
      application, refWatcher, configProvider
  )
  // 在這里會創(chuàng)建多個(gè)FragmentDestroyWatcher
  // 其內(nèi)部采用單例的方式,用List列表存儲FragmentDestroyWatcher
  // 而具體的FragmentDestroyWatcher其實(shí)就是SupportFragmentDestroyWatcher
  // SupportFragmentDestroyWatcher是其接口實(shí)現(xiàn)類
  // 這是androidx使用,但是需要fragment可以使用
  // FragmentDestroyWatcher中會監(jiān)聽生命周期的onActivityCreated方法
  // 遍歷所有的FragmentDestroyWatcher,調(diào)用其watchFragments方法
  // watchFragments方法在SupportFragmentDestroyWatcher的實(shí)現(xiàn),其實(shí)就是
  // 注冊該fragment的生命周期的監(jiān)聽
  FragmentDestroyWatcher.install( // 監(jiān)聽 Fragment.onDestroy()
      application, refWatcher, configProvider
  )
  // 初始化檢測內(nèi)存泄露過程中需要用到的對象
  listener.onLeakSentryInstalled(application) // Sentry 哨兵
}

這里的listener是LeakSentryListener接口,而實(shí)現(xiàn)LeakSentryListener接口的類,其實(shí)就是InternalLeakCanary,InternalLeakCanary是在leakcanary-android-core下的,InternalLeakCanary是單例模式的,采用的是kotlin單例,即用object關(guān)鍵字修飾類。

2.InternalLeakCanary#onLeakSentryInstalled

override fun onLeakSentryInstalled(application: Application) {
  this.application = application

  // 用于 heap dump:堆轉(zhuǎn)儲
  val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider) 

  val gcTrigger = GcTrigger.Default // 用于手動調(diào)用 GC

  val configProvider = { LeakCanary.config } // 配置項(xiàng)

  val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
  handlerThread.start()
  val backgroundHandler = Handler(handlerThread.looper) // 發(fā)起內(nèi)存泄漏檢測的線程

  // 堆轉(zhuǎn)存儲觸發(fā)器
  heapDumpTrigger = HeapDumpTrigger(
      application, backgroundHandler, LeakSentry.refWatcher, gcTrigger, heapDumper, configProvider
  )
  application.registerVisibilityListener { applicationVisible ->
    // 這里的applicationVisible其實(shí)就是調(diào)用擴(kuò)展函數(shù)registerVisibilityListener
    // 的時(shí)候,創(chuàng)建的VisibilityTracker對象傳入的listener變量
    // 在接收生命周期回調(diào)的時(shí)候,在onActivityStarted傳入true,
    // 在onActivityStopped傳入false
    // 這里這里的applicationVisible在onStarted的時(shí)候是true
    // 在onStopped的時(shí)候是false
    this.applicationVisible = applicationVisible
    // 在applicationVisible是false的時(shí)候,其內(nèi)部才會去檢查
    heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
  }
  // 這是添加動態(tài)快捷方式,即在手機(jī)桌面添加一個(gè)LeakCanary的快捷方式,在debug模式下
  addDynamicShortcut(application)
}
// 該方法主要是用于在手機(jī)桌面動態(tài)生成LeakCanary快捷方式
private fun addDynamicShortcut(application: Application) {
  // 如果系統(tǒng)版本小于25,則不添加動態(tài)快捷方式
  if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
    return
  }
  // 判斷是否允許添加動態(tài)快捷方式
  if (!application.resources.getBoolean(R.bool.leak_canary_add_dynamic_shortcut)) {
    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()) {
    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 {
      val applicationInfo = application.applicationInfo
      if (applicationInfo.labelRes != 0) {
        application.getString(applicationInfo.labelRes)
      } else {
        applicationInfo.nonLocalizedLabel.toString()
      }
    }
    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 = leakDisplayActivityIntent
  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) {
    CanaryLog.d(
        ignored,
        "Could not add dynamic shortcut. " +
            "shortcutCount=$shortcutCount, " +
            "maxShortcutCountPerActivity=${shortcutManager.maxShortcutCountPerActivity}"
    )
  }
}

四、FragmentDestroyWatcher.install

這里使用的RefWatcher對象,是在InternalLeakSentry中進(jìn)行初始化的,然后在調(diào)用ActivityDestroyWatcher和FragmentDestroyWatcher的install方法的時(shí)候,傳入。

internal interface FragmentDestroyWatcher {
    // 實(shí)現(xiàn)類是SupportFragmentDestroyWatcher

  fun watchFragments(activity: Activity)

  companion object {

    private const val SUPPORT_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"

    fun install(
      application: Application,
      refWatcher: RefWatcher,
      configProvider: () -> LeakSentry.Config
    ) {
      val fragmentDestroyWatchers = mutableListOf<FragmentDestroyWatcher>()

      if (SDK_INT >= O) { // >= 26,使用 AndroidOFragmentDestroyWatcher
        fragmentDestroyWatchers.add(
            AndroidOFragmentDestroyWatcher(refWatcher, configProvider)
        )
      }

      if (classAvailable(
              SUPPORT_FRAGMENT_CLASS_NAME
          )
      ) {
        fragmentDestroyWatchers.add( 
            // androidx 使用 SupportFragmentDestroyWatcher
            SupportFragmentDestroyWatcher(refWatcher, configProvider)
        )
      }

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

      application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacksAdapter() {
        override fun onActivityCreated(
          activity: Activity,
          savedInstanceState: Bundle?
        ) {
          // 遍歷所有的fragmentDestroyWatchers
          // 調(diào)用其watchFragments,其實(shí)就是調(diào)用SupportFragmentDestroyWatcher
          // 中的方法實(shí)現(xiàn)
          // 遍歷fragmentDestroyWatchers調(diào)用watchFragments的時(shí)候
          // 其實(shí)就是對fragment添加生命周期監(jiān)聽,用于在生命周期回調(diào)的時(shí)候調(diào)用RefWatcher.watch方法
          for (watcher in fragmentDestroyWatchers) {
            watcher.watchFragments(activity)
          }
        }
      })
    }

    private fun classAvailable(className: String): Boolean {
      return try {
        Class.forName(className)
        true
      } catch (e: ClassNotFoundException) {
        false
      }
    }
  }
}
internal class SupportFragmentDestroyWatcher(
  private val refWatcher: RefWatcher,
  private val configProvider: () -> Config
) : FragmentDestroyWatcher {

  // 這是注冊給fragment的生命周期監(jiān)聽的
  // 從這里可以看出,fragment銷毀的時(shí)候,其實(shí)也會調(diào)用RefWatcher.watch
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null && configProvider().watchFragmentViews) {
        refWatcher.watch(view)
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      if (configProvider().watchFragments) {
        refWatcher.watch(fragment)
      }
    }
  }

  override fun watchFragments(activity: Activity) {
    // 這里就是根據(jù)傳入的FragmentActivity,然后獲取supportFragmentManager
    // 對Fragment進(jìn)行生命周期的監(jiān)聽注冊
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
    }
  }
}

五、RefWatcher

在監(jiān)測Activity和Fragment的生命周期進(jìn)行內(nèi)存回收以及是否泄露的過程,就是調(diào)用RefWatcher.watch方法進(jìn)行,該方法是使用Synchronized修飾的同步方法。RefWatcher.watch的方法,一般是在Activity和Fragment生命周期執(zhí)行到onDestroy的時(shí)候調(diào)用。根據(jù)生命周期監(jiān)聽觸發(fā)回調(diào),然后調(diào)用RefWatcher.watch方法。

class RefWatcher constructor(
  private val clock: Clock,
  private val checkRetainedExecutor: Executor,
  private val onReferenceRetained: () -> Unit,
  /**
   * Calls to [watch] will be ignored when [isEnabled] returns false
   */
  private val isEnabled: () -> Boolean = { true }
) {

  /**
   * References passed to [watch] that haven't made it to [retainedReferences] yet.
   * watch() 方法傳進(jìn)來的引用,尚未判定為泄露
   */
  private val watchedReferences = mutableMapOf<String, KeyedWeakReference>()
  /**
   * References passed to [watch] that we have determined to be retained longer than they should
   * have been.
   * watch() 方法傳進(jìn)來的引用,已經(jīng)被判定為泄露
   */
  private val retainedReferences = mutableMapOf<String, KeyedWeakReference>()
  // 引用隊(duì)列,配合弱引用使用,當(dāng)弱引用中的對象被回收時(shí),接收弱引用對象
  private val queue = ReferenceQueue<Any>() 

  val hasRetainedReferences: Boolean
    @Synchronized get() {
      removeWeaklyReachableReferences()
      return retainedReferences.isNotEmpty()
    }

  val hasWatchedReferences: Boolean
    @Synchronized get() {
      removeWeaklyReachableReferences()
      return retainedReferences.isNotEmpty() || watchedReferences.isNotEmpty()
    }

  val retainedKeys: Set<String>
    @Synchronized get() {
      removeWeaklyReachableReferences()
      return HashSet(retainedReferences.keys)
    }

  /**
   * Identical to [.watch] with an empty string reference name.
   */
  @Synchronized fun watch(watchedReference: Any) {
    watch(watchedReference, "")
  }

  /**
   * Watches the provided references.
   *
   * @param referenceName An logical identifier for the watched object.
   */
  @Synchronized fun watch(
    watchedReference: Any,
    referenceName: String
  ) {
    if (!isEnabled()) {
      return
    }
    // 移除隊(duì)列中將要被 GC 的引用
    removeWeaklyReachableReferences() 
    val key = UUID.randomUUID()
        .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    // 構(gòu)建當(dāng)前引用的弱引用對象,并關(guān)聯(lián)引用隊(duì)列 queue
    // queue是一個(gè)ReferenceQueue對象,該對象是用來保存已經(jīng)回收了的對象的弱引用
    // 即構(gòu)建給對象構(gòu)建弱引用對象,當(dāng)對象被回收的時(shí)候,引用該對象的弱引用就會被加入到ReferenceQueue隊(duì)列的末尾
    val reference = 
      KeyedWeakReference(watchedReference, key, referenceName, watchUptimeMillis, queue)
    if (referenceName != "") {
      CanaryLog.d(
          "Watching instance of %s named %s with key %s", reference.className,
          referenceName, key
      )
    } else {
      CanaryLog.d(
          "Watching instance of %s with key %s", reference.className, key
      )
    }
    // 如果該對象尚未被判定為泄露,則將該弱引用加入到watchedReferences
    // 將引用存入 watchedReferences
    watchedReferences[key] = reference 
    // 這里采用線程池執(zhí)行
    // 該線程池的賦值是在InternalLeakSentry初始化RefWatcher對象的時(shí)候賦值的
    // 該線程池的內(nèi)部執(zhí)行是采用mainHandler的方式,切換到主線程進(jìn)行執(zhí)行
    /*
    private val checkRetainedExecutor = Executor { // 默認(rèn)五秒后執(zhí)行
      mainHandler.postDelayed(it, LeakSentry.config.watchDurationMillis)
    }
    LeakSentry.config.watchDurationMillis的定義是在LeakSentry
    val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5)
    */
    checkRetainedExecutor.execute {
      moveToRetained(key) // 如果當(dāng)前引用未被移除,仍在 watchedReferences  隊(duì)列中,
                          // 說明仍未被 GC,移入 retainedReferences 隊(duì)列中,暫時(shí)標(biāo)記為泄露
    }
  }

  @Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableReferences() // 再次調(diào)用,防止遺漏
    val retainedRef = watchedReferences.remove(key)
    if (retainedRef != null) {//說明可能存在內(nèi)存泄漏
      retainedReferences[key] = retainedRef
      onReferenceRetained()
    }
  }

  @Synchronized fun removeRetainedKeys(keysToRemove: Set<String>) {
    retainedReferences.keys.removeAll(keysToRemove)
  }

  @Synchronized fun clearWatchedReferences() {
    watchedReferences.clear()
    retainedReferences.clear()
  }

  private fun removeWeaklyReachableReferences() {
    // 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.
    // 弱引用一旦變得弱可達(dá),就會立即入隊(duì)。這將在 finalization 或者 GC 之前發(fā)生。
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference? // 隊(duì)列 queue 中的對象都是會被 GC 的
      if (ref != null) {//說明被釋放了
        val removedRef = watchedReferences.remove(ref.key)//獲取被釋放的引用的key
        if (removedRef == null) {
          retainedReferences.remove(ref.key)
        }
        // 移除 watchedReferences 隊(duì)列中的會被 GC 的 ref 對象,剩下的就是可能泄露的對象
      }
    } while (ref != null)
  }
}
  private fun removeWeaklyReachableReferences() {
    // 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.
    // 弱引用一旦變得弱可達(dá),就會立即入隊(duì)。這將在 finalization 或者 GC 之前發(fā)生。
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference? // 隊(duì)列 queue 中的對象都是會被 GC 的
      if (ref != null) {
        //說明queue中有弱引用對象,說明該弱引用對象引用的對象被釋放了
        // 獲取被釋放的引用的key
        // 從兩個(gè)map集合中進(jìn)行釋放
        val removedRef = watchedReferences.remove(ref.key)
        // 這里判斷為空的原因是,如果在watchedReferences中沒有該對象
        // 那么說明該對象已經(jīng)被判定為泄露,但是這個(gè)時(shí)候該對象又被回收了,
        // 所以得從這個(gè)判定泄露的集合中移除該判定
        if (removedRef == null) {
          retainedReferences.remove(ref.key)
        }
        // 移除 watchedReferences 隊(duì)列中的會被 GC 的 ref 對象,剩下的就是可能泄露的對象
      }
    } while (ref != null)
  }
  @Synchronized private fun moveToRetained(key: String) {
    // 再次調(diào)用,防止遺漏
    removeWeaklyReachableReferences() 
    // 如果移除ReferenceQueue隊(duì)列中的弱引用之后,
    // 在watchedReferences隊(duì)列中依然還有該對象,說明該弱引用引用的對象在此時(shí)依然不是弱可達(dá)
    // 此時(shí)就不會移除watchedReferences中的弱引用
    // 那么說明此時(shí)就存在內(nèi)存泄露的可能,則需要將watchedReferences中key對應(yīng)的弱引用
    // 加入到retainedReferences中,判斷該弱引用引用的對象是可能泄露的對象
    val retainedRef = watchedReferences.remove(key)
    if (retainedRef != null) {//說明可能存在內(nèi)存泄漏
      retainedReferences[key] = retainedRef
      onReferenceRetained()
    }
  }

六、VisibilityTracker

VisibilityTracker其實(shí)就是在InternalLeakCanary.onLeakSentryInstalled方法中通過調(diào)用application.registerVisibilityListener方法的時(shí)候,添加的Application.ActivityLifecycleCallbacks,這里采用適配器模式,使用適配器模式的目的,其實(shí)就是不需要重寫所有方法,只在VisibilityTracker中重寫需要使用的方法。
VisibilityTracker的目的其實(shí)就是監(jiān)聽Activity的生命周期變化,即是否是執(zhí)行到了onStart和onStop,如果是onStop的時(shí)候,則做內(nèi)存泄露監(jiān)測工作。
VisibilityTracker與ActivityDestroyWatcher有點(diǎn)區(qū)別,ActivityDestroyWatcher是最終Activity執(zhí)行onDestroy的時(shí)候進(jìn)行內(nèi)存泄露分析

internal class VisibilityTracker(
  private val listener: (Boolean) -> Unit
) :
    ActivityLifecycleCallbacksAdapter() {

  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

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

  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
      // 這里就是給InternalLeakCanary.onLeakSentryInstalled中注冊的application.registerVisibilityListener
      // 傳入的listener的回調(diào)傳入?yún)?shù)為false,表示需要進(jìn)行內(nèi)存泄露檢測
      listener.invoke(false)
    }
  }
}

internal fun Application.registerVisibilityListener(listener: (Boolean) -> Unit) {
  // 當(dāng)生命周期回調(diào)的時(shí)候,就會調(diào)用VisibilityTracker中重寫的方法
  // 而在VisibilityTracker中重寫了started和stopped兩個(gè)方法
  // 在started中調(diào)用listener的時(shí)候傳入的參數(shù)是true
  // 在stopped中調(diào)用listener的時(shí)候傳入的參數(shù)是false
  // 然后在listener這個(gè)接口實(shí)現(xiàn)的回調(diào)中就會接收該listener的參數(shù)
  // 在根據(jù)該參數(shù)判斷是否需要進(jìn)行內(nèi)存泄露監(jiān)測,這里就是回調(diào)到了
  // InternalLeakCanary.onLeakSentryInstalled中注冊的application.registerVisibilityListener
  // 進(jìn)而調(diào)用到了HeapDumpTrigger.onApplicationVisibilityChanged
  registerActivityLifecycleCallbacks(VisibilityTracker(listener))
}

七、HeapDumpTrigger#onApplicationVisibilityChanged

本方法是在InternalLeakCanary.onLeakSentryInstalled給application添加生命周期回調(diào)的時(shí)候,根據(jù)onStart和onStop生命周期的變化來進(jìn)行Heap Dump(heap dump文件(.hprof))
當(dāng)生命周期執(zhí)行到onStop的時(shí)候,會向該Application的擴(kuò)展函數(shù)registerVisibilityListener的參數(shù)listener這個(gè)高階函數(shù)傳入boolean參數(shù)為false
看InternalLeakCanary#onLeakSentryInstalled方法中對application添加的生命周期監(jiān)聽,這是調(diào)用了application的擴(kuò)展函數(shù),該擴(kuò)展函數(shù)是在VisibilityTracker中定義的。

    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }

其實(shí)registerVisibilityListener方法內(nèi)部調(diào)用的就是application的registerActivityLifecycleCallbacks方法,傳入的是Application.ActivityLifecycleCallbacks對象,這里傳入的是VisibilityTracker,其實(shí)VisibilityTracker就是Application.ActivityLifecycleCallbacks的子類實(shí)現(xiàn)。

internal fun Application.registerVisibilityListener(listener: (Boolean) -> Unit) {
  registerActivityLifecycleCallbacks(VisibilityTracker(listener))
}

HeapDumpTrigger.onApplicationVisibilityChanged方法的調(diào)用,就是根據(jù)上述傳給VisibilityTracker的listener函數(shù)來回調(diào)調(diào)用的,listener接收的是false的時(shí)候,就會調(diào)用scheduleRetainedInstanceCheck,接收的是false的時(shí)候是生命周期執(zhí)行到onStop的時(shí)候。

  fun onApplicationVisibilityChanged(applicationVisible: Boolean) {
    if (applicationVisible) {
      applicationInvisibleAt = -1L
    } else {
      applicationInvisibleAt = SystemClock.uptimeMillis()
      scheduleRetainedInstanceCheck("app became invisible", LeakSentry.config.watchDurationMillis)
    }
  }

這里的delayMillis默認(rèn)是5s,因?yàn)樵搮?shù)接收的是LeakSentry.config.watchDurationMillis,這個(gè)值初始默認(rèn)值是5s。

  private fun scheduleRetainedInstanceCheck(
    reason: String,
    delayMillis: Long // 默認(rèn) 5 s
  ) {
    if (checkScheduled) {
      return
    }
    checkScheduled = true
    // 通過handler切換到主線程調(diào)用,確保是在主線程執(zhí)行,并且延遲5S執(zhí)行。
    backgroundHandler.postDelayed({
      checkScheduled = false
      checkRetainedInstances(reason)
    }, delayMillis)
  }
  private fun checkRetainedInstances(reason: String) {
    CanaryLog.d("Checking retained instances because %s", reason)
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
      return
    }
    // RefWatcher.retainedKeys是一個(gè)Set集合,該Set集合是從
    // RefWatcher.retainedReferences中獲取的數(shù)據(jù)
    // RefWatcher.retainedReferences存儲的就是已經(jīng)被判定為泄露的
    var retainedKeys = refWatcher.retainedKeys

    // 當(dāng)前泄露實(shí)例個(gè)數(shù)小于 5 個(gè),不進(jìn)行 heap dump
    if (checkRetainedCount(retainedKeys, config.retainedVisibleThreshold)) return

    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
      showRetainedCountWithDebuggerAttached(retainedKeys.size)
      scheduleRetainedInstanceCheck("debugger was attached", WAIT_FOR_DEBUG_MILLIS)
      CanaryLog.d(
          "Not checking for leaks while the debugger is attached, will retry in %d ms",
          WAIT_FOR_DEBUG_MILLIS
      )
      return
    }

    // 可能存在被觀察的引用將要變得弱可達(dá),但是還未入隊(duì)引用隊(duì)列。
    // 這時(shí)候應(yīng)該主動調(diào)用一次 GC,可能可以避免一次 heap dump
    // 即被判定為內(nèi)存泄露的隊(duì)列中可能有些引用將要變成弱可達(dá)
    // 這個(gè)時(shí)候就是將被判定為泄露的一些對象,進(jìn)行再一次回收。
    gcTrigger.runGc()

    retainedKeys = refWatcher.retainedKeys

    if (checkRetainedCount(retainedKeys, config.retainedVisibleThreshold)) return
    // 為heap dump設(shè)置被判定為內(nèi)存泄露的對應(yīng)的key集合
    HeapDumpMemoryStore.setRetainedKeysForHeapDump(retainedKeys)

    CanaryLog.d("Found %d retained references, dumping the heap", retainedKeys.size)
    HeapDumpMemoryStore.heapDumpUptimeMillis = SystemClock.uptimeMillis()
    dismissNotification()
    // 輸出一個(gè)heap dump 文件
    val heapDumpFile = heapDumper.dumpHeap() // AndroidHeapDumper
    if (heapDumpFile == null) {
      CanaryLog.d("Failed to dump heap, will retry in %d ms", WAIT_AFTER_DUMP_FAILED_MILLIS)
      scheduleRetainedInstanceCheck("failed to dump heap", WAIT_AFTER_DUMP_FAILED_MILLIS)
      showRetainedCountWithHeapDumpFailed(retainedKeys.size)
      return
    }

    refWatcher.removeRetainedKeys(retainedKeys) // 移除已經(jīng) heap dump 的 retainedKeys

    HeapAnalyzerService.runAnalysis(application, heapDumpFile) // 分析 heap dump 文件
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 相信很多人知道LeakCanay是square公司出的一個(gè)內(nèi)存泄漏檢測開源庫,其使用也非常簡單,在Applicat...
    jxiang112閱讀 1,066評論 0 0
  • 簡介 LeakCanary是一款開源的內(nèi)存泄露檢測工具,可以用來檢測項(xiàng)目中的Activity是否能夠被GC及時(shí)回收...
    隕落凡間的星靈閱讀 697評論 1 6
  • LeakCanary是使用成本較低的HeapProfiler, 通常內(nèi)存泄漏都比較隱蔽, 和OOM后再去分析hp...
    ukyoo閱讀 2,526評論 0 7
  • 簡介 LeakCanary是一款開源的內(nèi)存泄漏檢查工具,在項(xiàng)目中,可以使用它來檢測Activity是否能夠被GC及...
    JasmineBen閱讀 29,917評論 7 46
  • 前言 最近有個(gè)想法——就是把 Android 主流開源框架進(jìn)行深入分析,然后寫成一系列文章,包括該框架的詳細(xì)使用與...
    wildma閱讀 1,338評論 0 2

友情鏈接更多精彩內(nèi)容