亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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

tips:本來是只想分析下內(nèi)存泄漏檢測部分,但寫著寫著就跑偏了,因為內(nèi)存泄漏的檢測難點在于對對象生命周期的把控, Leakcanary 對于 Service 生命周期的把控我覺得非常值得我們學習,并且在項目中也會用到。外加 Leakcanary 用 Kotlin 重寫,一些語法糖我平時也沒用過,就順便寫了下,整體讀下來有點啰嗦。

源碼版本

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

內(nèi)存泄漏

本博客著重分析 leakcanary 源碼實現(xiàn)原理以及一些優(yōu)秀設計,對于內(nèi)存泄漏的解釋就簡單用我自己的理解來解釋下:短生命周期對象持有長生命周期對象,當短生命周期對象需要被回收時因其持有長生命周期對象導致無法正常回收的情況;

源碼淺析

在了解其優(yōu)秀設計之前先來簡單分析下其源碼以及實現(xiàn)原理。

檢測原理

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

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

來檢測被弱引用的對象是否被正常回收、釋放,舉個例子:

// 定義類
class A
// 檢測的目標對象
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 開發(fā)中的 Activity、Fragment、Service、自定義 View 都是容易發(fā)生內(nèi)存泄漏的對象,Leakcanary 所做的工作就是在合適的時機(一般是在回收時,如 Activity 的 onDestory 后)對這些對象進行弱引用并且關聯(lián)引用隊列,根據(jù)其是否被添加到引用隊列來判斷是否發(fā)生泄漏。

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

初始化

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

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

當項目引入 Leakcanary 后打包出的 apk 的清單文件中可以找到注冊了MainProcessAppWatcherInstaller,其關鍵源碼部分如下:

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

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

AppWatcher.kt

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

appDefaultWatchers(application)

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

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

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

fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher // 注意這個 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)聽 rootview 內(nèi)存泄漏
    ServiceWatcher(reachabilityWatcher) // 用于監(jiān)聽 service 內(nèi)存泄漏
  )
}

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

val objectWatcher = ObjectWatcher(...)

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

LeakCanaryDelegate.loadLeakCanary(application)

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

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

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

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

}

實現(xiàn)細節(jié)

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

ObjectWatcher

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

AppWatcher.kt

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

我第一次看到這個clock = { SystemClock.uptimeMillis() } 是有點沒看懂,所以先來分析下這個寫法,先看看這個 Clock 源碼:

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

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

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

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

ActivityWatcher

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

class ActivityWatcher(
  private val application: Application,
  // 注意這里,傳進來的是 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)聽 Activity onDestory 生命周期方法
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

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

ObjectWatcher.kt

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

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

KeyedWeakReference

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

這個包裝相當于對 WeakReference 做了一些擴展,可以通過 key 從前面的 watchedObjects 中移除或者獲取。

moveToRetained

再來看看 moveToRetained 方法源碼:

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

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

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

InternalLeakCanary.kt

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

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

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

HeapDumpTrigger.kt

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

接著查看 checkRetainedObjects 方法源碼:

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

  val config = configProvider()
  // ...
  // 獲取仍然存在于 map 中沒有被回收的對象數(shù)量
  var retainedReferenceCount = objectWatcher.retainedObjectCount
  // 如果有再執(zhí)行一次 gc
  if (retainedReferenceCount > 0) {
    gcTrigger.runGc() // gc 操作,注意這里的實現(xiàn) 后面會貼源碼
    retainedReferenceCount = objectWatcher.retainedObjectCount // 再次獲取數(shù)量
  }
  // 里面是對app 是否在前臺,存活對象數(shù)量的判斷,retainedVisibleThreshold 默認是 5
  if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
  
  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  // 判斷上次堆轉儲時間間隔是否小于 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 就延遲對應時間在重新執(zhí)行該方法
    scheduleRetainedObjectCheck(
      delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
    )
    return
  }
  
  dismissRetainedCountNotification()
  val visibility = if (applicationVisible) "visible" else "not visible"
  // 開始堆轉儲
  dumpHeap(
    retainedReferenceCount = retainedReferenceCount,
    retry = true,
    reason = "$retainedReferenceCount retained objects, app is $visibility"
  )
}

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

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

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

來看看 Leakcanary 是如何獲取堆信息寫入文件的,dumpHeap 方法源碼:

private fun dumpHeap(
  retainedReferenceCount: Int,
  retry: Boolean,
  reason: String
) {
  // 注意這個 directoryProvider 并不是 ContentProvider 只是一個普通類
  val directoryProvider =
    InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
  // 新建堆轉儲文件,內(nèi)部包含一些權限、目錄等等邏輯判斷 比較簡單就不貼代碼了
  // 生成文件名: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 {
      // 這里是堆轉儲核心
      configProvider().heapDumper.dumpHeap(heapDumpFile)
    }
    // 觸發(fā)后面的分析方法
    InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))
  } catch (throwable: Throwable) {
    // 堆轉儲失敗
    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) 這行代碼是堆轉儲的核心,來看一下其源碼:

AndroidDebugHeapDumper.kt

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

堆轉儲的實現(xiàn)非常簡單,系統(tǒng)源碼中自帶了對應方法,傳入文件即可。

FragmentAndViewModelWatcher

接著來看看對于 Fragment 和 ViewModel 是如何進行檢測的,直接看源碼:

和 ActivityWatcher 如出一轍,同樣利用 Application 的 api 注冊對 Activity 的監(jiān)聽,在 Activity onCreate 生命周期時遍歷 fragmentDestoryWatchers 進行調(diào)用,接著來看看 fragmentDestoryWatchers 的定義:

private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
  // 注意這個 list 的類型,item 是函數(shù)式變量,相當于方法可以直接調(diào)用
  val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
  
  // sdk26 以上添加 AndroidOFragmentDestroyWatcher
  if (SDK_INT >= O) {
    fragmentDestroyWatchers.add(
      // reachabilityWatcher 是老朋友 objectWatcher 了
      AndroidOFragmentDestroyWatcher(reachabilityWatcher)
    )
  }
  // 通過反射獲取 AndroidXFragmentDestroyWatcher
  // 對應 androidX 的 fragment
  getWatcherIfAvailable(
    ANDROIDX_FRAGMENT_CLASS_NAME, // "androidx.fragment.app.Fragment"
    ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, // "AndroidXFragmentDestroyWatcher"
    reachabilityWatcher
  )?.let {
    fragmentDestroyWatchers.add(it)
  }
  
  // 通過反射獲取 AndroidSupportFragmentDestroyWatcher
  // 對應之前的 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
}

依次來看看上面定義的三個 item 實現(xiàn)細節(jié)。

AndroidOFragmentDestroyWatcher

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

AndroidXFragmentDestroyWatcher

和 AndroidOFragmentDestroyWatcher 一樣是通過 Activity 來監(jiān)聽 Fragment 的生命周期,差別在于注冊監(jiān)聽的 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)
      // 注意這里,多了一個針對 ViewModel 的處理
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
}

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

ViewModelClearedWatcher

直接看源碼:

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

  companion object {
    // 外部調(diào)用的 install 方法
    fun install(
      storeOwner: ViewModelStoreOwner, // 這里也就是 activity
      reachabilityWatcher: ReachabilityWatcher // objectWatcher
    ) {
      // 相當于再 Activity onCreate 方法中又創(chuàng)建了一個 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 字段
  // 這里面存儲著 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 銷毀時觸發(fā)的方法
  override fun onCleared() {
    viewModelMap?.values?.forEach { viewModel ->
      // 進行檢測
      reachabilityWatcher.expectWeaklyReachable(
        viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
      )
    }
  }
}

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

AndroidSupportFragmentDestroyWatcher

最后再來看一下 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

這個類的源碼呢,就不貼了,因為涉及到了 Square 的另一個庫 Curtains,這個庫的作用就是可以監(jiān)聽 window 的生命周期。很遺憾我對這個庫的了解也不到位,就不瞎說八道了。

不過這里可以稍微總結一下,對于監(jiān)聽對象是否泄漏很關鍵的一個點就是能夠監(jiān)聽到對象的生命周期,也就是能監(jiān)聽到對象的銷毀。

下一小節(jié)是值得我們學習的一個小節(jié),監(jiān)聽 Service 的銷毀,看下 Leakcanary 是如何做到的。

ServiceWatcher (重點 值得學習借鑒)

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

直接看源碼:

class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
    override fun install() {
      checkMainThread() // 檢測主線程
      // ...
      try {
        // 重點 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
              // 注意這個 activityThreadServices
              // 這個是反射獲取的 ActivityThread 中的 mServices 字段是 Map<IBinder, Service> 類型
              // 這個字段存放所有啟動的 Service
              activityThreadServices[key]?.let {
                // 這里是將要銷毀的 Service 進行存儲
                // key 是 IBinder 對象
                onServicePreDestroy(key, it)
              }
            }
            mCallback?.handleMessage(msg) ?: false
          }
        }
        // 重點 2:反射獲取 AMS (ActivityManagerService)
        swapActivityManager { activityManagerInterface, activityManagerInstance ->
          uninstallActivityManager = {
            swapActivityManager { _, _ ->
              activityManagerInstance
            }
          }
          // 對 AMS 進行動態(tài)代理 
          Proxy.newProxyInstance(
            activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
          ) { _, method, args ->
            // 判斷方法名是否是 serviceDoneExecuting 
            if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
              // 獲取 IBinder 對象
              val token = args!![0] as IBinder
              // 從上面存儲的 service 容器中查看是否存在對應 service
              if (servicesToBeDestroyed.containsKey(token)) {
                // 進行內(nèi)存泄漏檢測
                onServiceDestroyed(token)
              }
            }
            // ...
          }
        }
      } 
      // ...
    }
    
    private fun onServiceDestroyed(token: IBinder) {
      // 從 servicesToBeDestroyed 中移除
      servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
        serviceWeakReference.get()?.let { service ->
          // 利用 objectWatcher 進行內(nèi)存泄漏檢測
          reachabilityWatcher.expectWeaklyReachable(
            service, "${service::class.java.name} received Service#onDestroy() callback"
          )
        }
      }
    }
}

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

  • 反射 Activity 的 mH 的 mCallback 獲取即將銷毀的 Service 對象;
  • 對 AMS 進行動態(tài)代理監(jiān)聽 Service 的銷毀時機。

總結

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

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

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

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

相關文章

  • Android網(wǎng)絡請求庫android-async-http介紹

    Android網(wǎng)絡請求庫android-async-http介紹

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

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

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

    Android自定義半圓形圓盤滾動選擇器

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

    Android通過單點觸摸移動圖片

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

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

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

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

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

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

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

    Android檢測url地址是否可達的兩種方法

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

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

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

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

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

最新評論