开发者

源码浅析Android中内存泄漏检测工具Leakcanary的使用

开发者 https://www.devze.com 2023-04-22 10:18 出处:网络 作者: 孙先森Blog
目录前言源码版本内存泄漏源码浅析检测原理初始化appDefaultWatchers(application)LeakCanaryDelegate.loadLeakCanary(application)实现细节ObjectWatcherActivityWatcherFragmentAndViewModelWatcherRootViewWatche
目录
  • 前言
  • 源码版本
  • 内存泄漏
  • 源码浅析
  • 检测原理
  • 初始化
    • appDefaultWatchers(application)
    • LeakCanaryDelegate.loadLeakCanary(application)
  • 实现细节
    • ObjectWatcher
    • ActivityWatcher
    • FragmentAndViewModelWatcher
    • RootViewWatcher
    • ServiceWatcher (重点 值得学习借鉴)
  • 总结

    前言

    本博客将分析一下大名鼎鼎的 Leakcanary 想必作为 android 开发都多多少少接触过,新版本的 Leakcanary 也用 Kotlin 重写了一遍,最近详细查看了下源码,分享一下。

    tips:本来是只想分析下内存泄漏检测部分,但写着写着就跑偏了,因为内存泄漏的检测难点在于对对象生命周期的把控, Leakcanary 对于 Service 生命周期的把控我觉得非常值得我们学习,并且在项目中也会用到。外加 Leakcanary 用 Kotlin 重写,一些语法糖我平时也没用过,就顺便写了下,整体读下来有点啰嗦。

    源码版本

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

    内存泄漏

    本博客着重分析 leakcanary 源码实现原理以及一些优秀设计,对于内存泄漏的解释就简单用我自己的理解来解释下:短生命周期对象持有长生命周期对象,当短生命周期对象需要被回收时因其持有长生命周期对象导致无法正常回收的情况;

    源码浅析

    在了解其优秀设计之前先来简单分析下其源码以及实现原理。

    检测原理

    Leakcanary 检测内存泄漏的原理很简单,就是利用弱引用 WeakReference 的双参数构造方法

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

    来检测被弱引用的对象是否被正常回收、释放,举个例子:

    // 定义类
    class A
    // 检测的目标对象
    val obj = A()
    val queue = ReferenceQueue<A>()
    val weakObj = WeakReference(obj, queue)
    // 触发gc回收(注意:这样的操作不一定可以触发gc,具体如何触发gc 在下面的源码分析中有提到 leakcanary 是如何触发gc的)
    System.gc()
    
    val tmp = queue.poll()
    if (tmp === obj) {
        // 被回收
    } else {
        // 未回收
    }
    

    Android 开发中的 Activity、Fragment、Service、自定义 View 都是容易发生内存泄漏的对象,Leakcanary 所做的工作就是在合适的时机(一般是在回收时,如 Activity 的 onDestory 后)对这些对象进行弱引用并且关联引用队列,根据其是否被添加到引用队列来判断是否发生泄漏。

    关于判断一个对象是否发生泄漏的原理上面的示例代码已经简单演示,下面我们就顺着源码来看看 Leakcanary 的实现细节。

    初始化

    Leakcanary 仅需引入依赖即可完成初始化,放到现在这也不算多么神奇的技巧了,这是利用了 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
      }
      //..
    }
    

    可以看出调用了 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 遍历调用 install 
      watchersToInstall.forEach {
        it.install()
      }
      // 给 installCause 赋值,代表已经初始化
      installCause = RuntimeException("manualInstall() first called here")
    }
    

    appDefaultWatchers(application)

    上述初始化方法中第三个参数 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), // 用于监控activity内存泄漏
        FragmentAndViewModelWatcher(application, reachabilityWatcher),// 用于监控fragment,viewmodel 内存泄漏
        RootViewWatcher(reachabilityWatcher),// 用于监听 rootview 内存泄漏
        ServiceWatcher(reachabilityWatcher) // 用于监听 service 内存泄漏
      )
    }
    

    注意上述方法的第二个参数 reachabilityWatcher 默认赋值了 objectWatcher:

    val objectWatcher = ObjectWatcher(...)
    

    先记住他是 ObjectWatcher 类的实例,并且将其传递给了用于检测的各个 InstallableWatcher 实现类。

    LeakCanaryDelegate.loadLeakCanary(application)

    接着再回过头来看一下 LeakCanaryDelegate.loadLeakCanary(application) 这句代码,loadLeakCanary 作为 LeakCanaryDelegate 类中的一个函数类型变量,所以可以直接调用,看一下其源码:

    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) {
          // 出现异常时返回 NoLeakCanary
          NoLeakCanary
        }
      }
      // (Application) -> Unit 函数类型变量,接受一个 application 作为参数
      // 内部都是空实现
      object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
        override fun invoke(application: Application) {}
        override fun onObjectRetained() {}
      }
    }
    

    一般情况下 LeakCanaryDelegate.loadLeakCanary(application) 就相当于调用了 InternalLeakCanary,当然 InternalLeakCanary 也是 (Application) -> Unit 的实现了,对你没看错,函数类型不仅可以声明变量,也可以定义实现类,但需要实现 invoke 方法,看下其源码:

    internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
        override fun invoke(application: Application) {
          _application = application
          checkRunningInDebuggableBuild() // 检查是否是 debug 模式
          // 对 AppWatcher 的 objectWatcher 添加 listener
          AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
          // 这个用于触发 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 前后台切换时执行闭包中的逻辑 也就是调用堆转储的 onApplicationVisibilityChanged 方法
          application.registerVisibilityListener { applicationVisible ->
            this.applicationVisible = applicationVisible
            heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
          }
          // 这个方法代码比较简单利用 Application.registerActivityLifecycleCallbacks
          // 获取处于激活状态的 Activity 赋值给 resumedActivity
          registerResumedActivityListener(application)
          // 添加桌面快捷方式,也就是Leakcanary小黄鸟app图标,感兴趣的可以看一下 这里就不详细分析这个方法了
          addDynamicShortcut(application)
          // ...
        }
    
    }
    

    实现细节

    从初始化部分源码中可以看出对于内存泄漏监控的核心部分就是 appDefaultWatchers 方法中返回的四个InstallableWatcher 的实现类,下面我们依次来分析。

    ObjectWatcher

    在上述分析中四个实现类在初始化时都传入了 objectWatcher,所以有必要先来初步了解下:

    AppWatcher.kt

    val objectWatcher = ObjectWatcher(
      // 返回一个 Clock 对象
      clock = { SystemClock.uptimeMillis() },
      // Executor 对象
      checkRetainedExecutor = {
        // 切换到主线程延迟执行, retainedDelayMillis 默认是 5s
        mainHandler.postDelayed(it, retainedDelayMillis)
      },
      // 这个参数是 () -> Boolean 函数类型,默认是直接返回一个 true
      isEnabled = { true }
    )
    

    我第一次看到这个clock = { SystemClock.uptimeMillis() } 是有点没看懂,所以先来分析下这个写法,先看看这个 Clock 源码:

    // 注意这个 fun interface 是 kotlin 在 1.4 引入的 www.devze.com函数式接口
    // 接口中只能有一个未实现的方法
    fun interface Clock {
      fun uptimeMillis(): Long
      // ...
    }
    

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

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

    大概先了解下 objectWatcher 的参数部分,具体方法待下面用到时具体分析。

    ActivityWatcher

    先来看看我们最熟的 Activity 是如何进行内存泄漏监控的,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) {
            // 核心就在这一行代码里 这里也就是调用了 objectWatcher 的 expectWeaklyReachable 方法
            reachabilityWatcher.expectWeaklyReachable(
              activity, "${activity::class.java.name} received Activity#onDestroy() callback"
            )
          }
        }
    
      override fun install() {
        // 利用 Application 监听 Activity onDestory 生命周期方法
        application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
      }
    
      override fun uninstall() {
        application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
      }
    }
    

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

    ObjectWatcher.kt

    // 保存监控对象的 map
    private val watchedObjects = mutableMapOf<String, KeyedweakReference>()
    // 用于弱引用关联队列
    private val queue = ReferenceQueue<Any>()
    
    @Synchronized override fun expectWeaklyReachable(
      watchedObject: Any, /js/ 传入的 activity(已经 onDestory)
      description: String // 描述
    ) {
      if (!isEnabled()) { // 默认 isEnabled 返回 true,不会进入这个 if
        return
      }
      // 这个代码比较简单 我就简单说下作用
      // 当 watchedObject 成功被回收会添加进 queue 中,这个方法就是将添加到 queue 中的对象
      // 从 watchedObjects 中移除,被移除的说明没有发生内存泄漏
      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 执行
      checkRetainedExecutor.execute {
        // 主线程调用
        moveToRetained(key)
      }
    }
    

    KeyedWeakReference

    class KeyedWeakReference(
      referent: Any, // 监控对象,这里是 onDestory 的 Activity
      val key: String, // UUID 身份证
      val description: String, // 描述
      val watchUptimeMillis: Long, // 时间
      referenceQueue: ReferenceQueue<Any> // 当 referent 被回收会添加到这个队列中
    ) : WeakReference<Any>(referent, referenceQueue)
    //最后调用了 WeakReference 的双参数构造器
    

    这个包装相当于对 WeakReference 做了一些扩展,可以通过 key 从前面的 watchedObjects 中移除或者获取。

    moveToRetained

    再来看看 moveToRetained 方法源码:

    @Synchronized private fun moveToRetained(key: String) {
      // 再次尝试从 queue 中获取回收成功的对象,从 watcherObjects 中移除
      removeWeaklyReachableObjects()
      val retainedRef = watchedObjects[key]
      if (retainedRef != null) { // 如果还可以获取到代表回收失败 发生了内存泄漏
        retainedRef.retainedUptimeMillis = clock.uptimeMillis()
        // 遍历 onObjectRetainedListeners 对象调用其 onObjectRetained 方法
        onObjectRetainedListeners.forEach { it.onObjectRetained() }
      }
    }

    注意这个 onObjectRetainedListeners,在初始化的第二小节我们分析的 InternalLeakCanary 源码中有一处调用: AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

    这里的遍历调用一般情况下就相当于调用了 AppWatcher 的 onObjectRetained 方法,看下其源码部分:

    InternalLeakCanary.kt

    // 又调用了 scheduleRetajsinedObjectCheck 方法
    override fun onObjectRetained() = scheduleRetainedObjectCheck()
    
    fun scheduleRetainedObjectCheck() {
      if (this::heapDumpTrigger.isInitialized) {
        // 又调用到了 堆转储 对象里的 scheduleRetainedObjectCheck 方法
        heapDumpTrigger.scheduleRetainedObjectCheck()
      }
    }
    

    继续跟踪源码,查看 HeapDumpTrigger 类中的 scheduleRetainedObjectCheck 方法:

    HeapDumpTrigger.kt

    fun scheduleRetainedObjectCheck(
      delayMillis: Long = 0L
    ) {
      val checkCurrentlyScheduledAt = checkScheduledAt
      if (checkCurrentlyScheduledAt > 0) {
        return
      }
      checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
      // backgroundHandler 前面看过了 是一个 HandlerThread 这里是放到子线程执行
      backgroundHandler.postDelayed({
        checkScheduledAt = 0
        // 重点是这个方法
        checkRetainedObjects()
      }, delayMillis)
    }
    

    接着查看 checkRetainedObjects 方法源码:

    private fun checkRetainedObjects() {
      val iCanHasHeap = HeapDumpControl.iCanHasHeap()
    
      val config = configProvider()
      // ...
      // 获取仍然存在于 map 中没有被回收的对象数量
      var retainedReferenceCount = objectWatcher.retainedObjectCount
      // 如果有再执行一次 gc
      if (retainedReferenceCount > 0) {
        gcTrigger.runGc() // gc 操作,注意这里的实现 后面会贴源码
        retainedReferenceCount = objectWatcher.retainedObjectCount // 再次获取数量
      }
      // 里面是对app 是否在前台,存活对象数量的判断,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 就延迟对应时间在重新执行该方法
        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() 触发 gc 操作,这段代码看一下 Leakcanary 是如何实现的:

    object Default : GcTrigger {
      override fun runGc() {
        // 这段执行gc的代码 leakcanary 注释中表示是借鉴于 Android 系统源码片段
        // System.gc() 并不是每次执行都能触发gc,而 Runtime gc() 更容易触发
        // 这里的源码细节值得借鉴
        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)
      // 新建堆转储文件,内部包含一些权限、目录等等逻辑判断 比较简单就不贴代码了
      // 生成文件名: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(c开发者_JAV培训urrentEventUniqueId!!))
        // ...
        durationMillis = measureDurationMillis {
          // 这里是堆转储核心
          configProvider().heapDumper.dumpHeap(heapDumpFile)
        }
        // 触发后面的分析方法
        InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))
      } catch (throwable: Throwable) {
        // 堆转储失败
        InternalLeakCanary.sendEvent(HeapDumpFailed(currentEventUniqueId!!, throwable, retry))
        if (retry) { // 重试则延迟再执行一次方法
          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 方法实现
        Debug.dumpHprofData(heapDumpFile.absolutePath)
      }
    }
    

    堆转储的实现非常简单,系统源码中自带了对应方法,传入文件即可。

    FragmentAndViewModelWatcher

    接着来看看对于 Fragment 和 ViewModel 是如何进行检测的,直接看源码:

    源码浅析Android中内存泄漏检测工具Leakcanary的使用

    和 ActivityWatcher 如出一辙,同样利用 Application 的 api 注册对 Activity 的监听,在 Activity onCreate 生命周期时遍历 fragmentDestoryWatchers 进行调用,接着来看看 fragmentDestoryWatchers 的定义:

    private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
      // 注意这个 list 的类型,item 是函数式变量,相当于方法可以直接调用
      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 实现细节。

    AndroidOFragmentDestroyWatcher

    源码浅析Android中内存泄漏检测工具Leakcanary的使用

    reachabilityWatcher.expectWeaklyReachable() 方法在 ActiviyWatcher 小节中已经详细分析了,就不再赘述了。

    AndroidXFragmentDestroyWatcher

    和 AndroidOFragmentDestroyWatcher 一样是通过 Activity 来监听 Fragment 的生命周期,差别在于注册监听的 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 的监听,看一下其是如何实现的。

    ViewModelClearedWatcher

    直接看源码:

    // 注意这是一个 ViewModel 的子类
    internal class ViewModelClearedWatcher(
      storeOwner: ViewModelStoreOwner,
      private val reachabilityWatcher: ReachabilityWatcher
    ) : ViewModel() {
    
      companion object {
        // 外部调用的 install 方法
        fun install(
          storeOwner: ViewModelStoreOwner, // 这里也就是 activity
          reachabilityWatcher: ReachabilityWatcher // objectWatcher
        ) {
          // 相当于再 Activity onCreate 方法中又创建了一个 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 中创建的 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 销毁时触发的方法
      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,这个库的作用就是可以监听 window 的生命周期。很遗憾我对这个库的了解也不到位,就不瞎说八道了。

    不过这里可以稍微总结一下,对于监听对象是否泄漏很关键的一个点就是能够监听到对象的生命周期,也就是能监听到对象的销毁。

    下一小节是值得我们学习的一个小节,监听 Service 的销毁,看下 Leakcanary 是如何做到的。

    ServiceWatcher (重点 值得学习借鉴)

    对于 Service 的生命周期,我们都知道 Service 销毁时会走 onDestory,但是系统并没有提供像监听 Activity 的 Application.registerActivityLifecycleCallbacks 这样的 api,这一小节就重点分析下 Leakcanary 是如何做到的。

    直接看源码:

    class ServiceWatcher(private val reachabilityWatcher: Reachabil编程客栈ityWatcher) : 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 进行动态代理 
              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)) {
                    // 进行内存泄漏检测
                    onServiceDestroyed(token)
                  }
                }
                // ...
              }
            }
          } 
          // ...
        }
        
        private fun onServiceDestroyed(token: IBinder) {
          // 从 servicesToBeDestroyed 中移除
          servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
            serviceWeakReference.get()?.let { service ->
              // 利用 objectWatcher 进行内存泄漏检测
              reachabilityWatcher.expectWeaklyReachable(
                service, "${service::class.java.name} received Service#onDestroy() callback"
              )
            }
          }
        }
    }
    

    可以看出对于 Service 进行内存泄漏检测的核心有两步骤:

    • 反射 Activity 的 mH 的 mCallback 获取即将销毁的 Service 对象;
    • 对 AMS 进行动态代理监听 Service 的销毁时机。

    总结

    内存泄漏检测的原理非常简单,利用弱引用的双参数构造传入引用队列,掌握被检测对象的生命周期,在销毁时检查引用队列的情况。我们自己也能很轻松的写出对应 Demo,但 Leakcanary 中很多优秀设计非常值得学习,如:无感初始化(ContentProvider),一些 Kotlin 语法糖,对于 Service 生命周期的监听。

    尤其是最后对于 Service 生命周期的监听,通过反射系统类,结合动态代理很轻松就实现了,由此我们也可以延伸出对 Service 其他生命周期的监听。

    内存泄漏检测并不难,难点在于分析堆信息以及对于各种对象的生命周期把控。

    以上就是源码浅析Android中内存泄漏检测工具Leakcanary的使用的详细内容,更多关于Android Leakcanary内存泄漏检测的资料请关注我们其它相关文章!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消