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

这篇具有很好参考价值的文章主要介绍了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 方法,看下其源码:文章来源地址https://www.toymoban.com/news/detail-433542.html

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
   
    override fun invoke(application

到了这里,关于Android 源码浅析:Leakcanary 内存泄漏检测的好帮手的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 内存泄漏检测方式

    一 、 日志记录         通过宏定义重载了 malloc 和 free 函数,以在分配和释放内存的时候记录一些信息,包括文件名和行号,并将这些信息写入到相应的文件中。然后在 main 函数中演示了使用这些宏进行内存分配和释放。 _malloc 函数: 在分配内存之后,创建一个文件名,

    2024年01月17日
    浏览(38)
  • 项目性能优化-内存泄漏检测与修改

    最近终于有空优化一波项目的性能了,第一波借助Android Studio自带的Profiler工具检测内存泄漏。 右侧带有绿色原点的就是此时运行的Profiler的SESSION,点击右侧MEMORY进入内存监控的详情模块 第三步中抓取一段时间后,会自动停止,并打开Heap Dump文件 可以看到抓取到2个会导致内存

    2024年02月11日
    浏览(54)
  • iOS 内存泄漏检测 Instruments Leaks

    Xcode 中 按住 command + I 或者菜单栏 Product – Profile 2. 双击 Leaks 或者按 choose,打开 Leaks 面板 3. 在显示的 Leaks 面板中,点击左上角红色点,即可运行内存检测。 4. 在运行过程中如果发现Leak Checks(如图)出现红色X说明检测到内存泄露,将鼠标点击Leak Checks,在下方即可看到内存

    2024年02月01日
    浏览(44)
  • Python中的内存泄漏及其检测方法

    一、引言 内存泄漏是编程中常见的问题之一,它会导致程序在运行过程中不断消耗内存,最终可能导致程序崩溃或性能下降。在Python中,内存泄漏也是一个需要关注的问题。本文将详细介绍Python中的内存泄漏及其检测方法,以帮助读者更好地理解和解决这个问题。 二、Pyth

    2024年02月22日
    浏览(58)
  • 手写C语言的内存泄漏检测组件

    CC++语言中,栈空间有大小限制,所以程序员可以使用堆空间的内存。堆空间的内存是程序员自己申请的,需要程序员自己去调用释放的操作。内存管理是CC++程序员必须要注意的问题,其中包括了内存泄漏,内存泄漏的原因是程序中申请的内存没有进行释放,来看下面的例子

    2024年02月03日
    浏览(39)
  • Android之内存泄漏与内存溢出

    内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,导致系统无法及时回收内存并且分配给其他进程使用。通常少次数的内存无法及时回收并不会到程序造成什么影响,但是如果在内存本身就比较少获取多次导致内存无法正常回收时,就会导致内存

    2024年02月13日
    浏览(49)
  • 使用asan检测内存泄漏、堆栈溢出等问题

    操作过程参考:链接 缘起:程序在移动端崩溃,mac端复现不了,于是在写个崩溃位置函数的调用demo,使用ASAN工具进行排查。 验证过程 1、代码 main.cpp 使用附加ASAN工具的方式进行编译: 执行: 没有问题,以上是验证过程,如有问题执行时ASAN会提示有问题的相关位置。 介绍

    2024年02月11日
    浏览(63)
  • 如何处理Flutter内存泄漏检测和优化

    处理Flutter内存泄漏问题是构建高性能、稳定的应用程序的关键部分之一。在本文中,我将详细介绍如何检测和优化Flutter内存泄漏问题,以确保应用程序的良好性能和用户体验。 1. 了解内存泄漏 在深入了解如何处理Flutter内存泄漏之前,首先需要了解什么是内存泄漏。内存泄

    2024年04月14日
    浏览(41)
  • Android 内存泄漏

    内存泄漏:即memory leak。是指内存空间使用完毕后无法被释放的现象,虽然Java有垃圾回收机制(GC),但是对于还保持着引用, 该内存不能再被分配使用,逻辑上却已经不会再用到的对象,垃圾回收器不会回收它们。 内存溢出:即out of memory, 当你要求分配的内存超过了系统给你

    2024年02月08日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包