Android 源码浅析:Leakcanary 内存泄漏检测的好帮手

我们一起来分析一下大名鼎鼎的 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

猜你喜欢

转载自blog.csdn.net/m0_70748458/article/details/130485368