欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

源碼淺析Android中內(nèi)存泄漏檢測(cè)工具Leakcanary的使用

 更新時(shí)間:2023年04月21日 16:28:04   作者:孫先森Blog  
大名鼎鼎的 Leakcanary 想必作為 Android 開(kāi)發(fā)都多多少少接觸過(guò),新版本的 Leakcanary 也用 Kotlin 重寫(xiě)了一遍,最近詳細(xì)查看了下源碼,就來(lái)和大家簡(jiǎn)單分享一下

前言

本博客將分析一下大名鼎鼎的 Leakcanary 想必作為 Android 開(kāi)發(fā)都多多少少接觸過(guò),新版本的 Leakcanary 也用 Kotlin 重寫(xiě)了一遍,最近詳細(xì)查看了下源碼,分享一下。

tips:本來(lái)是只想分析下內(nèi)存泄漏檢測(cè)部分,但寫(xiě)著寫(xiě)著就跑偏了,因?yàn)閮?nèi)存泄漏的檢測(cè)難點(diǎn)在于對(duì)對(duì)象生命周期的把控, Leakcanary 對(duì)于 Service 生命周期的把控我覺(jué)得非常值得我們學(xué)習(xí),并且在項(xiàng)目中也會(huì)用到。外加 Leakcanary 用 Kotlin 重寫(xiě),一些語(yǔ)法糖我平時(shí)也沒(méi)用過(guò),就順便寫(xiě)了下,整體讀下來(lái)有點(diǎn)啰嗦。

源碼版本

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

內(nèi)存泄漏

本博客著重分析 leakcanary 源碼實(shí)現(xiàn)原理以及一些優(yōu)秀設(shè)計(jì),對(duì)于內(nèi)存泄漏的解釋就簡(jiǎn)單用我自己的理解來(lái)解釋下:短生命周期對(duì)象持有長(zhǎng)生命周期對(duì)象,當(dāng)短生命周期對(duì)象需要被回收時(shí)因其持有長(zhǎng)生命周期對(duì)象導(dǎo)致無(wú)法正?;厥盏那闆r;

源碼淺析

在了解其優(yōu)秀設(shè)計(jì)之前先來(lái)簡(jiǎn)單分析下其源碼以及實(shí)現(xiàn)原理。

檢測(cè)原理

Leakcanary 檢測(cè)內(nèi)存泄漏的原理很簡(jiǎn)單,就是利用弱引用 WeakReference 的雙參數(shù)構(gòu)造方法

WeakReference(T referent, ReferenceQueue<? super T> q)

來(lái)檢測(cè)被弱引用的對(duì)象是否被正?;厥铡⑨尫?,舉個(gè)例子:

// 定義類(lèi)
class A
// 檢測(cè)的目標(biāo)對(duì)象
val obj = A()
val queue = ReferenceQueue<A>()
val weakObj = WeakReference(obj, queue)
// 觸發(fā)gc回收(注意:這樣的操作不一定可以觸發(fā)gc,具體如何觸發(fā)gc 在下面的源碼分析中有提到 leakcanary 是如何觸發(fā)gc的)
System.gc()

val tmp = queue.poll()
if (tmp === obj) {
    // 被回收
} else {
    // 未回收
}

Android 開(kāi)發(fā)中的 Activity、Fragment、Service、自定義 View 都是容易發(fā)生內(nèi)存泄漏的對(duì)象,Leakcanary 所做的工作就是在合適的時(shí)機(jī)(一般是在回收時(shí),如 Activity 的 onDestory 后)對(duì)這些對(duì)象進(jìn)行弱引用并且關(guān)聯(lián)引用隊(duì)列,根據(jù)其是否被添加到引用隊(duì)列來(lái)判斷是否發(fā)生泄漏。

關(guān)于判斷一個(gè)對(duì)象是否發(fā)生泄漏的原理上面的示例代碼已經(jīng)簡(jiǎn)單演示,下面我們就順著源碼來(lái)看看 Leakcanary 的實(shí)現(xiàn)細(xì)節(jié)。

初始化

Leakcanary 僅需引入依賴(lài)即可完成初始化,放到現(xiàn)在這也不算多么神奇的技巧了,這是利用了 ContentProvider。

ContentProvider 的初始化時(shí)機(jī)在 Application 的 onCreate 之前,并且在 ContentProvider 的 onCreate 方法中可以獲取到 context、applicationContext。

當(dāng)項(xiàng)目引入 Leakcanary 后打包出的 apk 的清單文件中可以找到注冊(cè)了MainProcessAppWatcherInstaller,其關(guān)鍵源碼部分如下:

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

可以看出調(diào)用了 AppWatcher.manualInstall() 進(jìn)行了初始化,其源碼如下:

AppWatcher.kt

fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默認(rèn) 5s
  watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) // 獲取默認(rèn)Watchers 下面詳細(xì)分析
) {
  // 檢查是否在主線(xiàn)程
  // 原理:Looper.getMainLooper().thread === Thread.currentThread()
  checkMainThread()
  // ...
  this.retainedDelayMillis = retainedDelayMillis
  // 初始化 Shark 庫(kù)
  if (application.isDebuggableBuild) {
    LogcatSharkLog.install()
  }
  // 這行代碼下面詳細(xì)分析
  LeakCanaryDelegate.loadLeakCanary(application)
  // 對(duì) watchers 遍歷調(diào)用 install 
  watchersToInstall.forEach {
    it.install()
  }
  // 給 installCause 賦值,代表已經(jīng)初始化
  installCause = RuntimeException("manualInstall() first called here")
}

appDefaultWatchers(application)

上述初始化方法中第三個(gè)參數(shù) watchersToInstall 被賦予了默認(rèn)值,通過(guò) appDefaultWatchers 獲取了一個(gè) List<InstallableWatcher>,先看下 InstallableWatcher 源碼:

interface InstallableWatcher {
  fun install()
  fun uninstall()
}

是一個(gè)接口,定義了兩個(gè)方法看命名也能明白是安裝和卸載,接著看下 appDefaultWatchers 方法返回了什么:

fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher // 注意這個(gè) objectWatcher 很重要
): List<InstallableWatcher> {
  return listOf(
    ActivityWatcher(application, reachabilityWatcher), // 用于監(jiān)控activity內(nèi)存泄漏
    FragmentAndViewModelWatcher(application, reachabilityWatcher),// 用于監(jiān)控fragment,viewmodel 內(nèi)存泄漏
    RootViewWatcher(reachabilityWatcher),// 用于監(jiān)聽(tīng) rootview 內(nèi)存泄漏
    ServiceWatcher(reachabilityWatcher) // 用于監(jiān)聽(tīng) service 內(nèi)存泄漏
  )
}

注意上述方法的第二個(gè)參數(shù) reachabilityWatcher 默認(rèn)賦值了 objectWatcher:

val objectWatcher = ObjectWatcher(...)

先記住他是 ObjectWatcher 類(lèi)的實(shí)例,并且將其傳遞給了用于檢測(cè)的各個(gè) InstallableWatcher 實(shí)現(xiàn)類(lèi)。

LeakCanaryDelegate.loadLeakCanary(application)

接著再回過(guò)頭來(lái)看一下 LeakCanaryDelegate.loadLeakCanary(application) 這句代碼,loadLeakCanary 作為 LeakCanaryDelegate 類(lèi)中的一個(gè)函數(shù)類(lèi)型變量,所以可以直接調(diào)用,看一下其源碼:

internal object LeakCanaryDelegate {
  // 延遲初始化
  val loadLeakCanary by lazy {
    try {
      // 默認(rèn)加載 InternalLeakCanary 獲取其 INSTANCE 字段
      // InternalLeakCanary 是一個(gè) object class,編譯為 java 后會(huì)自動(dòng)生成 INSTANCE
      // 就是一個(gè)單例類(lèi) 這里是獲取其單例對(duì)象
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null) as (Application) -> Unit
    } catch (ignored: Throwable) {
      // 出現(xiàn)異常時(shí)返回 NoLeakCanary
      NoLeakCanary
    }
  }
  // (Application) -> Unit 函數(shù)類(lèi)型變量,接受一個(gè) application 作為參數(shù)
  // 內(nèi)部都是空實(shí)現(xiàn)
  object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    override fun invoke(application: Application) {}
    override fun onObjectRetained() {}
  }
}

一般情況下 LeakCanaryDelegate.loadLeakCanary(application) 就相當(dāng)于調(diào)用了 InternalLeakCanary,當(dāng)然 InternalLeakCanary 也是 (Application) -> Unit 的實(shí)現(xiàn)了,對(duì)你沒(méi)看錯(cuò),函數(shù)類(lèi)型不僅可以聲明變量,也可以定義實(shí)現(xiàn)類(lèi),但需要實(shí)現(xiàn) invoke 方法,看下其源碼:

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    override fun invoke(application: Application) {
      _application = application
      checkRunningInDebuggableBuild() // 檢查是否是 debug 模式
      // 對(duì) AppWatcher 的 objectWatcher 添加 listener
      AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
      // 這個(gè)用于觸發(fā) gc 回收,下面會(huì)分析一下
      val gcTrigger = GcTrigger.Default
      // 獲取相關(guān)配置
      val configProvider = { LeakCanary.config }
      // 啟動(dòng)了一個(gè) HandlerThread
      val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
      handlerThread.start()
      val backgroundHandler = Handler(handlerThread.looper)
      // 初始化堆轉(zhuǎn)儲(chǔ)對(duì)象,上面定義的一些變量也都傳入了進(jìn)去
      heapDumpTrigger = HeapDumpTrigger(
        application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,
        configProvider
      )
      // Leakcanary 對(duì) Application 的一個(gè)擴(kuò)展方法
      // 這里的作用是在 App 前后臺(tái)切換時(shí)執(zhí)行閉包中的邏輯 也就是調(diào)用堆轉(zhuǎn)儲(chǔ)的 onApplicationVisibilityChanged 方法
      application.registerVisibilityListener { applicationVisible ->
        this.applicationVisible = applicationVisible
        heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
      }
      // 這個(gè)方法代碼比較簡(jiǎn)單利用 Application.registerActivityLifecycleCallbacks
      // 獲取處于激活狀態(tài)的 Activity 賦值給 resumedActivity
      registerResumedActivityListener(application)
      // 添加桌面快捷方式,也就是Leakcanary小黃鳥(niǎo)app圖標(biāo),感興趣的可以看一下 這里就不詳細(xì)分析這個(gè)方法了
      addDynamicShortcut(application)
      // ...
    }

}

實(shí)現(xiàn)細(xì)節(jié)

從初始化部分源碼中可以看出對(duì)于內(nèi)存泄漏監(jiān)控的核心部分就是 appDefaultWatchers 方法中返回的四個(gè)InstallableWatcher 的實(shí)現(xiàn)類(lèi),下面我們依次來(lái)分析。

ObjectWatcher

在上述分析中四個(gè)實(shí)現(xiàn)類(lèi)在初始化時(shí)都傳入了 objectWatcher,所以有必要先來(lái)初步了解下:

AppWatcher.kt

val objectWatcher = ObjectWatcher(
  // 返回一個(gè) Clock 對(duì)象
  clock = { SystemClock.uptimeMillis() },
  // Executor 對(duì)象
  checkRetainedExecutor = {
    // 切換到主線(xiàn)程延遲執(zhí)行, retainedDelayMillis 默認(rèn)是 5s
    mainHandler.postDelayed(it, retainedDelayMillis)
  },
  // 這個(gè)參數(shù)是 () -> Boolean 函數(shù)類(lèi)型,默認(rèn)是直接返回一個(gè) true
  isEnabled = { true }
)

我第一次看到這個(gè)clock = { SystemClock.uptimeMillis() } 是有點(diǎn)沒(méi)看懂,所以先來(lái)分析下這個(gè)寫(xiě)法,先看看這個(gè) Clock 源碼:

// 注意這個(gè) fun interface 是 kotlin 在 1.4 引入的 函數(shù)式接口
// 接口中只能有一個(gè)未實(shí)現(xiàn)的方法
fun interface Clock {
  fun uptimeMillis(): Long
  // ...
}

clock = { SystemClock.uptimeMillis() } 就等價(jià)于以下代碼:

clock = object : Clock{
    override fun uptimeMillis(): Long {
        SystemClock.uptimeMillis()
    }
}

大概先了解下 objectWatcher 的參數(shù)部分,具體方法待下面用到時(shí)具體分析。

ActivityWatcher

先來(lái)看看我們最熟的 Activity 是如何進(jìn)行內(nèi)存泄漏監(jiān)控的,ActivityWatcher 源碼如下:

class ActivityWatcher(
  private val application: Application,
  // 注意這里,傳進(jìn)來(lái)的是 AppWatcher 的 objectWatcher
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        // 核心就在這一行代碼里 這里也就是調(diào)用了 objectWatcher 的 expectWeaklyReachable 方法
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

  override fun install() {
    // 利用 Application 監(jiān)聽(tīng) Activity onDestory 生命周期方法
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

直接查看 ObjectWatcher 的 expectWeaklyReachable 方法源碼:

ObjectWatcher.kt

// 保存監(jiān)控對(duì)象的 map
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
// 用于弱引用關(guān)聯(lián)隊(duì)列
private val queue = ReferenceQueue<Any>()

@Synchronized override fun expectWeaklyReachable(
  watchedObject: Any, // 傳入的 activity(已經(jīng) onDestory)
  description: String // 描述
) {
  if (!isEnabled()) { // 默認(rèn) isEnabled 返回 true,不會(huì)進(jìn)入這個(gè) if
    return
  }
  // 這個(gè)代碼比較簡(jiǎn)單 我就簡(jiǎn)單說(shuō)下作用
  // 當(dāng) watchedObject 成功被回收會(huì)添加進(jìn) queue 中,這個(gè)方法就是將添加到 queue 中的對(duì)象
  // 從 watchedObjects 中移除,被移除的說(shuō)明沒(méi)有發(fā)生內(nèi)存泄漏
  removeWeaklyReachableObjects()
  // 生成id (相當(dāng)于對(duì)象的身份證)
  val key = UUID.randomUUID()
    .toString()
  // 獲取時(shí)間戳
  val watchUptimeMillis = clock.uptimeMillis()
  // 將對(duì)象以及一些信息包裝為 KeyedWeakReference
  val reference =
    KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
  // ...
  // 包裝好的對(duì)象放入 watchedObjects 這個(gè) map 中
  watchedObjects[key] = reference
  // checkRetainedExecutor 別忘了,就是切換到主線(xiàn)程延遲 5s 執(zhí)行
  checkRetainedExecutor.execute {
    // 主線(xiàn)程調(diào)用
    moveToRetained(key)
  }
}

KeyedWeakReference

class KeyedWeakReference(
  referent: Any, // 監(jiān)控對(duì)象,這里是 onDestory 的 Activity
  val key: String, // UUID 身份證
  val description: String, // 描述
  val watchUptimeMillis: Long, // 時(shí)間
  referenceQueue: ReferenceQueue<Any> // 當(dāng) referent 被回收會(huì)添加到這個(gè)隊(duì)列中
) : WeakReference<Any>(referent, referenceQueue)
//最后調(diào)用了 WeakReference 的雙參數(shù)構(gòu)造器

這個(gè)包裝相當(dāng)于對(duì) WeakReference 做了一些擴(kuò)展,可以通過(guò) key 從前面的 watchedObjects 中移除或者獲取。

moveToRetained

再來(lái)看看 moveToRetained 方法源碼:

@Synchronized private fun moveToRetained(key: String) {
  // 再次嘗試從 queue 中獲取回收成功的對(duì)象,從 watcherObjects 中移除
  removeWeaklyReachableObjects()
  val retainedRef = watchedObjects[key]
  if (retainedRef != null) { // 如果還可以獲取到代表回收失敗 發(fā)生了內(nèi)存泄漏
    retainedRef.retainedUptimeMillis = clock.uptimeMillis()
    // 遍歷 onObjectRetainedListeners 對(duì)象調(diào)用其 onObjectRetained 方法
    onObjectRetainedListeners.forEach { it.onObjectRetained() }
  }
}

注意這個(gè) onObjectRetainedListeners,在初始化的第二小節(jié)我們分析的 InternalLeakCanary 源碼中有一處調(diào)用: AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

這里的遍歷調(diào)用一般情況下就相當(dāng)于調(diào)用了 AppWatcher 的 onObjectRetained 方法,看下其源碼部分:

InternalLeakCanary.kt

// 又調(diào)用了 scheduleRetainedObjectCheck 方法
override fun onObjectRetained() = scheduleRetainedObjectCheck()

fun scheduleRetainedObjectCheck() {
  if (this::heapDumpTrigger.isInitialized) {
    // 又調(diào)用到了 堆轉(zhuǎn)儲(chǔ) 對(duì)象里的 scheduleRetainedObjectCheck 方法
    heapDumpTrigger.scheduleRetainedObjectCheck()
  }
}

繼續(xù)跟蹤源碼,查看 HeapDumpTrigger 類(lèi)中的 scheduleRetainedObjectCheck 方法:

HeapDumpTrigger.kt

fun scheduleRetainedObjectCheck(
  delayMillis: Long = 0L
) {
  val checkCurrentlyScheduledAt = checkScheduledAt
  if (checkCurrentlyScheduledAt > 0) {
    return
  }
  checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
  // backgroundHandler 前面看過(guò)了 是一個(gè) HandlerThread 這里是放到子線(xiàn)程執(zhí)行
  backgroundHandler.postDelayed({
    checkScheduledAt = 0
    // 重點(diǎn)是這個(gè)方法
    checkRetainedObjects()
  }, delayMillis)
}

接著查看 checkRetainedObjects 方法源碼:

private fun checkRetainedObjects() {
  val iCanHasHeap = HeapDumpControl.iCanHasHeap()

  val config = configProvider()
  // ...
  // 獲取仍然存在于 map 中沒(méi)有被回收的對(duì)象數(shù)量
  var retainedReferenceCount = objectWatcher.retainedObjectCount
  // 如果有再執(zhí)行一次 gc
  if (retainedReferenceCount > 0) {
    gcTrigger.runGc() // gc 操作,注意這里的實(shí)現(xiàn) 后面會(huì)貼源碼
    retainedReferenceCount = objectWatcher.retainedObjectCount // 再次獲取數(shù)量
  }
  // 里面是對(duì)app 是否在前臺(tái),存活對(duì)象數(shù)量的判斷,retainedVisibleThreshold 默認(rèn)是 5
  if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
  
  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  // 判斷上次堆轉(zhuǎn)儲(chǔ)時(shí)間間隔是否小于 60s
  if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
    onRetainInstanceListener.onEvent(DumpHappenedRecently)
    showRetainedCountNotification(
      objectCount = retainedReferenceCount,
      contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
    )
    // 小于60s 就延遲對(duì)應(yīng)時(shí)間在重新執(zhí)行該方法
    scheduleRetainedObjectCheck(
      delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
    )
    return
  }
  
  dismissRetainedCountNotification()
  val visibility = if (applicationVisible) "visible" else "not visible"
  // 開(kāi)始堆轉(zhuǎn)儲(chǔ)
  dumpHeap(
    retainedReferenceCount = retainedReferenceCount,
    retry = true,
    reason = "$retainedReferenceCount retained objects, app is $visibility"
  )
}

注意上述的 gcTrigger.runGc() 觸發(fā) gc 操作,這段代碼看一下 Leakcanary 是如何實(shí)現(xiàn)的:

object Default : GcTrigger {
  override fun runGc() {
    // 這段執(zhí)行g(shù)c的代碼 leakcanary 注釋中表示是借鑒于 Android 系統(tǒng)源碼片段
    // System.gc() 并不是每次執(zhí)行都能觸發(fā)gc,而 Runtime gc() 更容易觸發(fā)
    // 這里的源碼細(xì)節(jié)值得借鑒
    Runtime.getRuntime().gc()
    enqueueReferences()
    System.runFinalization()
  }
  // ...
}

到這里源碼還剩下兩個(gè)部分:1. 獲取堆信息;2. 分析堆信息;先說(shuō)下第二點(diǎn) Leakcanary 在 2.x 之前的版本使用 haha 這個(gè)庫(kù)來(lái)解析 .hprof 文件,而 2.x 之后另起爐灶用 shark 庫(kù)來(lái)完成這一工作,很遺憾我對(duì) shark 的庫(kù)不夠了解,這部分就暫時(shí)不深入分析了。

來(lái)看看 Leakcanary 是如何獲取堆信息寫(xiě)入文件的,dumpHeap 方法源碼:

private fun dumpHeap(
  retainedReferenceCount: Int,
  retry: Boolean,
  reason: String
) {
  // 注意這個(gè) directoryProvider 并不是 ContentProvider 只是一個(gè)普通類(lèi)
  val directoryProvider =
    InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
  // 新建堆轉(zhuǎn)儲(chǔ)文件,內(nèi)部包含一些權(quán)限、目錄等等邏輯判斷 比較簡(jiǎn)單就不貼代碼了
  // 生成文件名:fileName = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS'.hprof'", Locale.US).format(Date())
  val heapDumpFile = directoryProvider.newHeapDumpFile()

  val durationMillis: Long
  if (currentEventUniqueId == null) {
    currentEventUniqueId = UUID.randomUUID().toString()
  }
  try {
    // 通知欄 Event
    InternalLeakCanary.sendEvent(DumpingHeap(currentEventUniqueId!!))
    // ...
    durationMillis = measureDurationMillis {
      // 這里是堆轉(zhuǎn)儲(chǔ)核心
      configProvider().heapDumper.dumpHeap(heapDumpFile)
    }
    // 觸發(fā)后面的分析方法
    InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))
  } catch (throwable: Throwable) {
    // 堆轉(zhuǎn)儲(chǔ)失敗
    InternalLeakCanary.sendEvent(HeapDumpFailed(currentEventUniqueId!!, throwable, retry))
    if (retry) { // 重試則延遲再執(zhí)行一次方法
      scheduleRetainedObjectCheck(
        delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
      )
    }
    // 通知欄展示
    showRetainedCountNotification(
      objectCount = retainedReferenceCount,
      contentText = application.getString(
        R.string.leak_canary_notification_retained_dump_failed
      )
    )
    return
  }
}

configProvider().heapDumper.dumpHeap(heapDumpFile) 這行代碼是堆轉(zhuǎn)儲(chǔ)的核心,來(lái)看一下其源碼:

AndroidDebugHeapDumper.kt

object AndroidDebugHeapDumper : HeapDumper {
  override fun dumpHeap(heapDumpFile: File) {
    // 利用 Android 本身的 Debug dumpHprofData 方法實(shí)現(xiàn)
    Debug.dumpHprofData(heapDumpFile.absolutePath)
  }
}

堆轉(zhuǎn)儲(chǔ)的實(shí)現(xiàn)非常簡(jiǎn)單,系統(tǒng)源碼中自帶了對(duì)應(yīng)方法,傳入文件即可。

FragmentAndViewModelWatcher

接著來(lái)看看對(duì)于 Fragment 和 ViewModel 是如何進(jìn)行檢測(cè)的,直接看源碼:

和 ActivityWatcher 如出一轍,同樣利用 Application 的 api 注冊(cè)對(duì) Activity 的監(jiān)聽(tīng),在 Activity onCreate 生命周期時(shí)遍歷 fragmentDestoryWatchers 進(jìn)行調(diào)用,接著來(lái)看看 fragmentDestoryWatchers 的定義:

private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
  // 注意這個(gè) list 的類(lèi)型,item 是函數(shù)式變量,相當(dāng)于方法可以直接調(diào)用
  val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
  
  // sdk26 以上添加 AndroidOFragmentDestroyWatcher
  if (SDK_INT >= O) {
    fragmentDestroyWatchers.add(
      // reachabilityWatcher 是老朋友 objectWatcher 了
      AndroidOFragmentDestroyWatcher(reachabilityWatcher)
    )
  }
  // 通過(guò)反射獲取 AndroidXFragmentDestroyWatcher
  // 對(duì)應(yīng) androidX 的 fragment
  getWatcherIfAvailable(
    ANDROIDX_FRAGMENT_CLASS_NAME, // "androidx.fragment.app.Fragment"
    ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, // "AndroidXFragmentDestroyWatcher"
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  }
  
  // 通過(guò)反射獲取 AndroidSupportFragmentDestroyWatcher
  // 對(duì)應(yīng)之前的 android support 包下的 Fragment
  getWatcherIfAvailable(
    ANDROID_SUPPORT_FRAGMENT_CLASS_NAME, // "android.support.v4.app.Fragment"
    ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, // "AndroidSupportFragmentDestroyWatcher"
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  }
  // 返回
  fragmentDestroyWatchers
}

依次來(lái)看看上面定義的三個(gè) item 實(shí)現(xiàn)細(xì)節(jié)。

AndroidOFragmentDestroyWatcher

reachabilityWatcher.expectWeaklyReachable() 方法在 ActiviyWatcher 小節(jié)中已經(jīng)詳細(xì)分析了,就不再贅述了。

AndroidXFragmentDestroyWatcher

和 AndroidOFragmentDestroyWatcher 一樣是通過(guò) Activity 來(lái)監(jiān)聽(tīng) Fragment 的生命周期,差別在于注冊(cè)監(jiān)聽(tīng)的 api :

internal class AndroidXFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
  
  // 和上面的 AndroidOFragmentDestroyWatcher 一樣 就不貼代碼了
  private val fragmentLifecycleCallbacks = // ...

  override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      // 和 AndroidOFragmentDestroyWatcher 的原理是一樣的
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      // 注意這里,多了一個(gè)針對(duì) ViewModel 的處理
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
}

這里增加了對(duì) ViewModel 的監(jiān)聽(tīng),看一下其是如何實(shí)現(xiàn)的。

ViewModelClearedWatcher

直接看源碼:

// 注意這是一個(gè) ViewModel 的子類(lèi)
internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {

  companion object {
    // 外部調(diào)用的 install 方法
    fun install(
      storeOwner: ViewModelStoreOwner, // 這里也就是 activity
      reachabilityWatcher: ReachabilityWatcher // objectWatcher
    ) {
      // 相當(dāng)于再 Activity onCreate 方法中又創(chuàng)建了一個(gè) ViewModel
      val provider = ViewModelProvider(storeOwner, object : Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T =
          ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
      })
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
  
  // 反射 activity 中的 ViewModelStore 中的 mMap 字段
  // 這里面存儲(chǔ)著 activity 中創(chuàng)建的 ViewModel
  private val viewModelMap: Map<String, ViewModel>? = try {
    val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
    mMapField.isAccessible = true
    mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
  } catch (ignored: Exception) {
    null
  }
  
  // ViewModel 銷(xiāo)毀時(shí)觸發(fā)的方法
  override fun onCleared() {
    viewModelMap?.values?.forEach { viewModel ->
      // 進(jìn)行檢測(cè)
      reachabilityWatcher.expectWeaklyReachable(
        viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
      )
    }
  }
}

如果對(duì) ViewModel 這部分不理解的可以看下我之前對(duì)于 ViewModel 源碼分析的博客 ————Android 源碼淺析:Jetpack 組件 —— ViewModel。

AndroidSupportFragmentDestroyWatcher

最后再來(lái)看一下 AndroidSupportFragmentDestroyWatcher,猜也能猜到和上面的邏輯是一樣的,僅僅是為了支持 AndroidSupport 包:

internal class AndroidSupportFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
  // 還是和上面一樣的
  private val fragmentLifecycleCallbacks = // ...

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

RootViewWatcher

這個(gè)類(lèi)的源碼呢,就不貼了,因?yàn)樯婕暗搅?Square 的另一個(gè)庫(kù) Curtains,這個(gè)庫(kù)的作用就是可以監(jiān)聽(tīng) window 的生命周期。很遺憾我對(duì)這個(gè)庫(kù)的了解也不到位,就不瞎說(shuō)八道了。

不過(guò)這里可以稍微總結(jié)一下,對(duì)于監(jiān)聽(tīng)對(duì)象是否泄漏很關(guān)鍵的一個(gè)點(diǎn)就是能夠監(jiān)聽(tīng)到對(duì)象的生命周期,也就是能監(jiān)聽(tīng)到對(duì)象的銷(xiāo)毀。

下一小節(jié)是值得我們學(xué)習(xí)的一個(gè)小節(jié),監(jiān)聽(tīng) Service 的銷(xiāo)毀,看下 Leakcanary 是如何做到的。

ServiceWatcher (重點(diǎn) 值得學(xué)習(xí)借鑒)

對(duì)于 Service 的生命周期,我們都知道 Service 銷(xiāo)毀時(shí)會(huì)走 onDestory,但是系統(tǒng)并沒(méi)有提供像監(jiān)聽(tīng) Activity 的 Application.registerActivityLifecycleCallbacks 這樣的 api,這一小節(jié)就重點(diǎn)分析下 Leakcanary 是如何做到的。

直接看源碼:

class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
    override fun install() {
      checkMainThread() // 檢測(cè)主線(xiàn)程
      // ...
      try {
        // 重點(diǎn) 1: 反射 ActivityThread 的 mH 中的 mCallback
        // 也就是 Handler 的 mCallback
        swapActivityThreadHandlerCallback { mCallback ->
          uninstallActivityThreadHandlerCallback = {
            swapActivityThreadHandlerCallback {
              mCallback
            }
          }
          Handler.Callback { msg ->
            if (msg.obj !is IBinder) {
              return@Callback false
            }
            if (msg.what == STOP_SERVICE) { // AMS Service onDestory 的 message 的 what
              val key = msg.obj as IBinder
              // 注意這個(gè) activityThreadServices
              // 這個(gè)是反射獲取的 ActivityThread 中的 mServices 字段是 Map<IBinder, Service> 類(lèi)型
              // 這個(gè)字段存放所有啟動(dòng)的 Service
              activityThreadServices[key]?.let {
                // 這里是將要銷(xiāo)毀的 Service 進(jìn)行存儲(chǔ)
                // key 是 IBinder 對(duì)象
                onServicePreDestroy(key, it)
              }
            }
            mCallback?.handleMessage(msg) ?: false
          }
        }
        // 重點(diǎn) 2:反射獲取 AMS (ActivityManagerService)
        swapActivityManager { activityManagerInterface, activityManagerInstance ->
          uninstallActivityManager = {
            swapActivityManager { _, _ ->
              activityManagerInstance
            }
          }
          // 對(duì) AMS 進(jìn)行動(dòng)態(tài)代理 
          Proxy.newProxyInstance(
            activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
          ) { _, method, args ->
            // 判斷方法名是否是 serviceDoneExecuting 
            if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
              // 獲取 IBinder 對(duì)象
              val token = args!![0] as IBinder
              // 從上面存儲(chǔ)的 service 容器中查看是否存在對(duì)應(yīng) service
              if (servicesToBeDestroyed.containsKey(token)) {
                // 進(jìn)行內(nèi)存泄漏檢測(cè)
                onServiceDestroyed(token)
              }
            }
            // ...
          }
        }
      } 
      // ...
    }
    
    private fun onServiceDestroyed(token: IBinder) {
      // 從 servicesToBeDestroyed 中移除
      servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
        serviceWeakReference.get()?.let { service ->
          // 利用 objectWatcher 進(jìn)行內(nèi)存泄漏檢測(cè)
          reachabilityWatcher.expectWeaklyReachable(
            service, "${service::class.java.name} received Service#onDestroy() callback"
          )
        }
      }
    }
}

可以看出對(duì)于 Service 進(jìn)行內(nèi)存泄漏檢測(cè)的核心有兩步驟:

  • 反射 Activity 的 mH 的 mCallback 獲取即將銷(xiāo)毀的 Service 對(duì)象;
  • 對(duì) AMS 進(jìn)行動(dòng)態(tài)代理監(jiān)聽(tīng) Service 的銷(xiāo)毀時(shí)機(jī)。

總結(jié)

內(nèi)存泄漏檢測(cè)的原理非常簡(jiǎn)單,利用弱引用的雙參數(shù)構(gòu)造傳入引用隊(duì)列,掌握被檢測(cè)對(duì)象的生命周期,在銷(xiāo)毀時(shí)檢查引用隊(duì)列的情況。我們自己也能很輕松的寫(xiě)出對(duì)應(yīng) Demo,但 Leakcanary 中很多優(yōu)秀設(shè)計(jì)非常值得學(xué)習(xí),如:無(wú)感初始化(ContentProvider),一些 Kotlin 語(yǔ)法糖,對(duì)于 Service 生命周期的監(jiān)聽(tīng)。

尤其是最后對(duì)于 Service 生命周期的監(jiān)聽(tīng),通過(guò)反射系統(tǒng)類(lèi),結(jié)合動(dòng)態(tài)代理很輕松就實(shí)現(xiàn)了,由此我們也可以延伸出對(duì) Service 其他生命周期的監(jiān)聽(tīng)。

內(nèi)存泄漏檢測(cè)并不難,難點(diǎn)在于分析堆信息以及對(duì)于各種對(duì)象的生命周期把控。

以上就是源碼淺析Android中內(nèi)存泄漏檢測(cè)工具Leakcanary的使用的詳細(xì)內(nèi)容,更多關(guān)于Android Leakcanary內(nèi)存泄漏檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android網(wǎng)絡(luò)請(qǐng)求庫(kù)android-async-http介紹

    Android網(wǎng)絡(luò)請(qǐng)求庫(kù)android-async-http介紹

    這篇文章主要介紹了Android網(wǎng)絡(luò)請(qǐng)求庫(kù)android-async-http介紹,本文講解了android-async-http的概念、特征以及使用實(shí)例,需要的朋友可以參考下
    2015-06-06
  • android實(shí)現(xiàn)緩存圖片等數(shù)據(jù)

    android實(shí)現(xiàn)緩存圖片等數(shù)據(jù)

    本文給大家分享的是Android采用LinkedHashMap自帶的LRU 算法緩存數(shù)據(jù)的方法和示例,有需要的小伙伴可以參考下。
    2015-07-07
  • Android自定義半圓形圓盤(pán)滾動(dòng)選擇器

    Android自定義半圓形圓盤(pán)滾動(dòng)選擇器

    這篇文章主要為大家詳細(xì)介紹了Android自定義半圓形圓盤(pán)滾動(dòng)選擇器 ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片

    Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片

    這篇文章主要為大家詳細(xì)介紹了Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Android顯示富文本+夜間深色模式

    Android顯示富文本+夜間深色模式

    大家好,本篇文章主要講的是Android顯示富文本+夜間深色模式,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽
    2022-01-01
  • Android實(shí)現(xiàn)美女拼圖游戲詳解

    Android實(shí)現(xiàn)美女拼圖游戲詳解

    這篇文章給大家用實(shí)例介紹了利用Android如何實(shí)現(xiàn)美女拼圖游戲,對(duì)大家開(kāi)發(fā)拼圖游戲具有一定的參考借鑒價(jià)值,感興趣的朋友們一起來(lái)看看吧。
    2016-09-09
  • Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解

    Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解

    這篇文章主要為大家介紹了Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Android檢測(cè)url地址是否可達(dá)的兩種方法

    Android檢測(cè)url地址是否可達(dá)的兩種方法

    今天小編就為大家分享一篇Android檢測(cè)url地址是否可達(dá)的兩種方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • android仿微信表情雨下落效果的實(shí)現(xiàn)方法

    android仿微信表情雨下落效果的實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于android仿微信表情雨下落效果的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • android自定義Camera實(shí)現(xiàn)錄像和拍照

    android自定義Camera實(shí)現(xiàn)錄像和拍照

    這篇文章主要為大家詳細(xì)介紹了android自定義Camera實(shí)現(xiàn)錄像和拍照功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05

最新評(píng)論