本篇文章主要是分析
LeakCanary
一个不起眼的类:BackgroundTrigger
。当出现内存泄漏时,这个类可以帮助我们当应用处于后台状态时再执行相关的dump heap操作。
依赖如下:
implementation 'com.squareup.leakcanary:leakcanary-android-release:2.9.1'
还没分析这个类之前相信大家下意识的通过下面两种方式实现前后台监听:
-
通过
Application.registerActivityLifecycleCallbacks()
添加回调的方式来分析当前应用处于前台还是后台 -
使用
Application
级别的ProcessLifecycleOwner
添加Observer
的方式监听前后台
但是,BackgroundTrigger
不是完全这么实现的,并且其监听应用是否切换到后台状态
和上面两种方式监听应用切换到后台状态
,这两个后台状态一些情况不是等价
的,比如:
上面两种监听到后台状态,而
BackgroundTrigger
不一定处于所监听的后台状态,而BackgroundTrigger
监听到的后台状态,一定意味着上面两种方式监听也是处于后台状态。
除此之外,BackgroundTrigger
还有一个其他的狠活非常值得大家学习一下,接下来我们开始分析。
监听应用是否处于后台
这里我们从BackgroundTrigger
的入口方法start()
分析:
fun start() {
checkMainThread()
backgroundListener.install(application)
}
先检测是否为主线程,不是会抛出异常,接下来调用backgroundListener
的install()
方法,我们先看下backgroundListener
是个啥:
![](https://img.haomeiwen.com/i27208505/a493f3056ed17cff.png)
可以看到backgroundListener
就是一个BackgroundListener
类型,并且实现了ActivityLifecycleCallbacks
接口:
![](https://img.haomeiwen.com/i27208505/778420539abc1865.png)
接下来就要分析BackgroundListener
的install()
方法:
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
}
- 调用
ActivityManager
的getMyMemoryState()
方法,memoryOutState
是一个RunningAppProcessInfo
类型:
![](https://img.haomeiwen.com/i27208505/5ae4e084bbba95ab.png)
通过注释可以看到获取当前这个应用进程的内存状态信息,并填充信息到当前传入的RunningAppProcessInfo
对现象中,并指明了填充的哪些字段(没指明的不进行填充):
![](https://img.haomeiwen.com/i27208505/c4ac1dce464d5501.png)
-
然后使用被填充的
RunningAppProcessInfo
的importance
判断,如果大于等于RunningAppProcessInfo.IMPORTANCE_BACKGROUND(400)
就认为处于后台状态:这个400的状态代表着当前应用进程应用不再积极活跃的运行我们相关的组件了:
![](https://img.haomeiwen.com/i27208505/204de47d3153448d.png)
也就意味着并不是当我们home掉应用,就认为其处于后台状态,只有其处于一个非常不活跃的响应状态才认定其处于后台进程状态,这也就是开头说的那两种方式监听的后台状态和BackgroundTrigger
监听到的后台状态不同的地方。
`后者这样实现的目的,就是为了尽可能减少dump heap对我们应用进程的影响。`
我们回到checkAppInBackground
,可以看到其执行isImportanceBackground
后台状态检测后,会再次延迟5s再去通过postDelay()
去调用自身检测是否处于后台状态。
动态代理优化接口方法的重写
之前有写过这样的一篇文章:接口使用额外重写的无关方法太多?优化它,就是为了解决,当我们实现某个接口时只想重写其中的一个必须的方法,而不是重写所有的方法(包括无关的)。
然后BackgroundTrigger
来了一个狠活,通过动态代理的方式创建一个接口的实现类对象并委托给要实现这个接口的类
,BackgroundListener
就是一个典型的例子:
![](https://img.haomeiwen.com/i27208505/3a396082819b4114.png)
上面说过,BackgroundListener
实现了ActivityLifecycleCallbacks
接口,这个接口要重写的方法可是非常多,但是我们看看BackgroundListener
重写了几个方法:
![](https://img.haomeiwen.com/i27208505/7b430b46e8660b5c.png)
就实现了其中的两个方法,其他的一个都没实现 ,关键就是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
网友评论