我报名参加金石方案1期挑战——分割10万奖池,这是我的第2篇文章,点击查看活动详情
本篇文章主要是剖析
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
是个啥:
能够看到backgroundListener
便是一个BackgroundListener
类型,而且完成了ActivityLifecycleCallbacks
接口:
接下来就要剖析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
类型:经过注释能够看到获取当时这个运用进程的内存状况信息,并填充信息到当时传入的
RunningAppProcessInfo
对现象中,并指明晰填充的哪些字段(没指明的不进行填充): -
然后运用被填充的
RunningAppProcessInfo
的importance
判别,如果大于等于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
时会大大削减对咱们运用的影响,以及最终面的黑科技:动态代理的办法创建一个接口的完成类目标并托付给要完成这个接口的类
,削减接口不需要的办法的重写。