肢解LeakCanary:精通BackgroundTrigger的“狠活”

123 篇文章 7 订阅
57 篇文章 0 订阅

本篇文章主要是分析LeakCanary一个不起眼的类:BackgroundTrigger。当出现内存泄漏时,这个类可以帮助我们当应用处于后台状态时再执行相关的dump heap操作。

依赖如下:

implementation 'com.squareup.leakcanary:leakcanary-android-release:2.9.1'

还没分析这个类之前相信大家下意识的通过下面两种方式实现前后台监听:

  1. 通过Application.registerActivityLifecycleCallbacks()添加回调的方式来分析当前应用处于前台还是后台

  2. 使用Application级别的ProcessLifecycleOwner添加Observer的方式监听前后台

但是,BackgroundTrigger不是完全这么实现的,并且其监听应用是否切换到后台状态和上面两种方式监听应用切换到后台状态,这两个后台状态一些情况不是等价的,比如:

上面两种监听到后台状态,而BackgroundTrigger不一定处于所监听的后台状态,而BackgroundTrigger监听到的后台状态,一定意味着上面两种方式监听也是处于后台状态。

除此之外,BackgroundTrigger还有一个其他的狠活非常值得大家学习一下,接下来我们开始分析。

监听应用是否处于后台

这里我们从BackgroundTrigger的入口方法start()分析:

fun start() {
  checkMainThread()
  backgroundListener.install(application)
}

先检测是否为主线程,不是会抛出异常,接下来调用backgroundListenerinstall()方法,我们先看下backgroundListener是个啥:

可以看到backgroundListener就是一个BackgroundListener类型,并且实现了ActivityLifecycleCallbacks接口:

接下来就要分析BackgroundListenerinstall()方法:

fun install(application: Application) {
  application.registerActivityLifecycleCallbacks(this)
  ...
  checkAppInBackground.run()
}

由于BackgroundListener实现了ActivityLifecycleCallbacks,所以这里通过传递过来的Application注入到监听应用所有Activity生命周期回调的一个回调集合中,这个大家狠熟悉了。

先前我说过BackgroundTrigger并不是完全通过registerActivityLifecycleCallbacks实现了,但是还是借助了其实现。

下面我们直接看下BackgroundTrigger实现重写的onActivityPaused()监听方法:

override fun onActivityPaused(activity: Activity) {
  mainHandler.removeCallbacks(checkAppInBackground)
  //BACKGROUND_DELAY_MS为1s
  mainHandler.postDelayed(checkAppInBackground, BACKGROUND_DELAY_MS)
}

通过Hanlder延迟1s执行一个checkAppInBackground

private val checkAppInBackground: Runnable = object : Runnable {
  override fun run() {
    val appInBackgroundNow = processInfo.isImportanceBackground
    updateBackgroundState(appInBackgroundNow)
    if (!appInBackgroundNow) {
      mainHandler.removeCallbacks(this)
      mainHandler.postDelayed(this, BACKGROUND_REPEAT_DELAY_MS)
    }
  }
}

关键代码就是processInfo.isImportanceBackground,这个就是本文要讲如何监听应用是否已经切换到后台状态的:

override val isImportanceBackground: Boolean
  get() {
    ActivityManager.getMyMemoryState(memoryOutState)
    return memoryOutState.importance >= RunningAppProcessInfo.IMPORTANCE_BACKGROUND
  }
  1. 调用ActivityManagergetMyMemoryState()方法,memoryOutState是一个RunningAppProcessInfo类型:

通过注释可以看到获取当前这个应用进程的内存状态信息,并填充信息到当前传入的RunningAppProcessInfo对现象中,并指明了填充的哪些字段(没指明的不进行填充):

  1. 然后使用被填充的RunningAppProcessInfoimportance判断,如果大于等于RunningAppProcessInfo.IMPORTANCE_BACKGROUND(400)就认为处于后台状态:

    这个400的状态代表着当前应用进程应用不再积极活跃的运行我们相关的组件了:

也就意味着并不是当我们home掉应用,就认为其处于后台状态,只有其处于一个非常不活跃的响应状态才认定其处于后台进程状态,这也就是开头说的那两种方式监听的后台状态和BackgroundTrigger监听到的后台状态不同的地方。

`后者这样实现的目的,就是为了尽可能减少dump heap对我们应用进程的影响。`

我们回到checkAppInBackground,可以看到其执行isImportanceBackground后台状态检测后,会再次延迟5s再去通过postDelay()去调用自身检测是否处于后台状态。

动态代理优化接口方法的重写

之前有写过这样的一篇文章:接口使用额外重写的无关方法太多?优化它,就是为了解决,当我们实现某个接口时只想重写其中的一个必须的方法,而不是重写所有的方法(包括无关的)。

然后BackgroundTrigger来了一个狠活,通过动态代理的方式创建一个接口的实现类对象并委托给要实现这个接口的类BackgroundListener就是一个典型的例子:

上面说过,BackgroundListener实现了ActivityLifecycleCallbacks接口,这个接口要重写的方法可是非常多,但是我们看看BackgroundListener重写了几个方法:

就实现了其中的两个方法,其他的一个都没实现 ,关键就是BackgroundListener将要实现的ActivityLifecycleCallbacks接口委托给了noOpDelegate返回的实现类,我们看下这个方法:

internal inline fun <reified T : Any> noOpDelegate(): T = leakcanary.internal.noOpDelegate()

internal inline fun <reified T : Any> noOpDelegate(): T {
  val javaClass = T::class.java
  return Proxy.newProxyInstance(
    javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER
  ) as T
}

private val NO_OP_HANDLER = InvocationHandler { _, _, _ ->
  // no op
}

这就是科技与狠话啊,真是万万没想到哈。利用内联函数的特性,借助于泛型实化,通过动态代理的方式创建一个ActivityLifecycleCallbacks的实现类。

总结

本篇文章主要是讲解了BackgroundTrigger如何监听后台状态的,相比较于传统的方式,这种监听后台状态的方式更加严格,进行dump heap时会大大减少对我们应用的影响,以及最后面的黑科技:动态代理的方式创建一个接口的实现类对象并委托给要实现这个接口的类,减少接口不需要的方法的重写。

作者:长安皈故里
链接:https://juejin.cn/post/7148457048064131079

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值