LeakCanery作为Android中检测内存泄漏的利器, 已经被广泛使用了很长时间了, 核心原理也有很多博文细讲, 大概内容就是,
在对象需要被回收时, 将此对象放入WeakReference中, WeakReference 有个带两个参数的构造函数, 第一个参数是要放入的对象, 第二个参数是一个引用队列,当对象被GC时,当前的WeakReference会被放入到该队列:
图片.png
例如:
当Activity调用onDestory的时候,说明这个Activity需要被回收了, 这个时候把这个Activity放入WeakReference , 再过几秒(GC有间隔时间)检查引用队列中有没有这个WeakReference, 如果有, 说明Activity被回收了, 如果没有,则表示内存泄漏了。
可参考文章:
https://juejin.cn/post/7084115960793137165
这篇文章主要记录, LeakCanary监控“哪些对象“的销毁以及怎么监控这些对象的“销毁时机“,也就是说在什么时候将要销毁的对象放入WeakReference 中观察。
先看结论:
1.Activity : onDestory
2.Fragment:onFragmentViewDestroyed、onFragmentDestroyed
3.View : onViewDetachedFromWindow
4.Service: serviceDoneExecuting
下面再跟着结论,查看源码
LeakCanary新版都不用在Application中初始化了, 转而换成在ContentProvider中初始化, 只需要引入库就直接可用了,很是方便, 那么既然知道了初始化入口,就先出入口开始看起:
图片.png
可以看到,在入口处调用了AppWatcher.manualInstall(application) 方法, 继续跟进
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
checkMainThread()
if (isInstalled) {
throw IllegalStateException(
"AppWatcher already installed, see exception cause for prior install call", installCause
)
}
check(retainedDelayMillis >= 0) {
"retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
}
installCause = RuntimeException("manualInstall() first called here")
this.retainedDelayMillis = retainedDelayMillis
if (application.isDebuggableBuild) {
LogcatSharkLog.install()
}
// Requires AppWatcher.objectWatcher to be set
LeakCanaryDelegate.loadLeakCanary(application)
watchersToInstall.forEach {
it.install()
}
}
前面都是一些前置判断, 对吼的循环才是重点, 而循环的值是一个默认值, 看一下默认值有什么:
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
可以看到, 默认值里有四个Watcher, 这四个Watcher就对应了要观察什么对象, 和上面的结论对应了, 接下来就看他们各自怎么监控对象的销魂时机的
1.Activity
图片.png
可以看到,LeakCanary通过注册全局生命周期监听, 在OnDestory时监控Activity是否被GC
2.Fragment
图片.png
FragmentAndViewModelWatcher用一个变量fragmentDestroyWatchers存储了三个 参数为Activity无返回值的方法, 这三个方法分别是什么呢?
第一个:
internal class AndroidOFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null) {
reachabilityWatcher.expectWeaklyReachable(
view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
"(references to its views should be cleared to prevent leaks)"
)
}
}
override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
override fun invoke(activity: Activity) {
val fragmentManager = activity.fragmentManager
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
}
}
这个类实现了一个Function的接口, 重写了invoke方法, 放此方法被调用时, 此Fragment会注册一个全局生命周期监听,在onFragmentViewDestroyed时,监控Fragment的视图View是否被GC, 在onFragmentDestroyed监控Fragment是否被GC。
第二个、第三个:
图片.png
后两个调用的是同一个方法,方法里面根据参数来生成不同的类, 那么看看这两个类是什么:
图片.png
图片.png 图片.png
这两个类的作用其实和第一个一样, 从名称就可以看出,这两个类只是用来适配AndroidX和Support包的。
3.View
class RootViewWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val listener = OnRootViewAddedListener { rootView ->
val trackDetached = when(rootView.windowType) {
PHONE_WINDOW -> {
when (rootView.phoneWindow?.callback?.wrappedCallback) {
// Activities are already tracked by ActivityWatcher
is Activity -> false
is Dialog -> rootView.resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
// Probably a DreamService
else -> true
}
}
// Android widgets keep detached popup window instances around.
POPUP_WINDOW -> false
TOOLTIP, TOAST, UNKNOWN -> true
}
if (trackDetached) {
rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
val watchDetachedView = Runnable {
reachabilityWatcher.expectWeaklyReachable(
rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
)
}
override fun onViewAttachedToWindow(v: View) {
mainHandler.removeCallbacks(watchDetachedView)
}
override fun onViewDetachedFromWindow(v: View) {
mainHandler.post(watchDetachedView)
}
})
}
}
override fun install() {
Curtains.onRootViewsChangedListeners += listener
}
override fun uninstall() {
Curtains.onRootViewsChangedListeners -= listener
}
}
View也是类似, 通过监听View的AttachState, 在onViewDetachedFromWindow时, 监控View是否被GC, 不过此处用了mainHandler将操作post到了主线程中执行。
4.Service
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }
private val activityThreadInstance by lazy {
activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
}
private val activityThreadServices by lazy {
val mServicesField =
activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }
@Suppress("UNCHECKED_CAST")
mServicesField[activityThreadInstance] as Map<IBinder, Service>
}
private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
private var uninstallActivityManager: (() -> Unit)? = null
override fun install() {
checkMainThread()
check(uninstallActivityThreadHandlerCallback == null) {
"ServiceWatcher already installed"
}
check(uninstallActivityManager == null) {
"ServiceWatcher already installed"
}
try {
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
Handler.Callback { msg ->
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
activityThreadServices[key]?.let {
onServicePreDestroy(key, it)
}
}
mCallback?.handleMessage(msg) ?: false
}
}
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
activityManagerInstance
}
}
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
onServiceDestroyed(token)
}
}
try {
if (args == null) {
method.invoke(activityManagerInstance)
} else {
method.invoke(activityManagerInstance, *args)
}
} catch (invocationException: InvocationTargetException) {
throw invocationException.targetException
}
}
}
} catch (ignored: Throwable) {
SharkLog.d(ignored) { "Could not watch destroyed services" }
}
}
override fun uninstall() {
checkMainThread()
uninstallActivityManager?.invoke()
uninstallActivityThreadHandlerCallback?.invoke()
uninstallActivityManager = null
uninstallActivityThreadHandlerCallback = null
}
private fun onServicePreDestroy(
token: IBinder,
service: Service
) {
servicesToBeDestroyed[token] = WeakReference(service)
}
private fun onServiceDestroyed(token: IBinder) {
servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
serviceWeakReference.get()?.let { service ->
reachabilityWatcher.expectWeaklyReachable(
service, "${service::class.java.name} received Service#onDestroy() callback"
)
}
}
}
private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
val mHField =
activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
val mH = mHField[activityThreadInstance] as Handler
val mCallbackField =
Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
val mCallback = mCallbackField[mH] as Handler.Callback?
mCallbackField[mH] = swap(mCallback)
}
@SuppressLint("PrivateApi")
private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
val singletonClass = Class.forName("android.util.Singleton")
val mInstanceField =
singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }
val singletonGetMethod = singletonClass.getDeclaredMethod("get")
val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
"android.app.ActivityManager" to "IActivityManagerSingleton"
} else {
"android.app.ActivityManagerNative" to "gDefault"
}
val activityManagerClass = Class.forName(className)
val activityManagerSingletonField =
activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]
// Calling get() instead of reading from the field directly to ensure the singleton is
// created.
val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)
val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
mInstanceField[activityManagerSingletonInstance] =
swap(iActivityManagerInterface, activityManagerInstance!!)
}
companion object {
private const val STOP_SERVICE = 116
private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
}
}
ServiceWatcher内容比较多, 慢慢看
在install的时候, 先调用了swapActivityThreadHandlerCallback方法
图片.png
图片.png
图片.png
这个方法的作用就是,先通过反射,获取到ActivityThread中的mH, 这个mH是ActivityThread用来处理消息的Handler, 然后将这个Handler中的CallBack替换成自己写的CallBack:
mCallbackField[mH] = swap(mCallback)
替换以后, 这个Handler接收的消息,都会被自己写的CallBack拦截, 从而可以根据消息类型进行想要的操作:
自己写的CallBack, 根据msg.what执行操作:
图片.png
图片.png
116这个值,是停止服务时会收到的, 可以在ActivityThread中找到对应的地方:
图片.png
然而在接收到stopService消息之后, 还没有进行回收监控,只是调用了onServicePreDestroy, 将这个IBinder放进了集合里
图片.png
继续往下:
图片.png
接着调用了swapActivityManager方法:
图片.png
这个方法主要干了三件事:
1.实例化一个ActivityManager (通过单例获取,保证对象唯一性)
2.获取IActivityManager接口
3.将ActivityManager 中的mInstance替换成自己写的Manager
然后通过动态代理的方式, 代理IActivityManager中的serviceDoneExecuting方法
图片.png
图片.png
而serviceDoneExecuting方法,在ActivityThread接到stopService的指令后,会在处理方法中调用, 而且在Service调用完onDestory后:
图片.png
到这一步,表示Service需要被回收了, 于是调用onServiceDestroyed监控Service是否被GC。
图片.png
总的来看,相对复杂的就是Service的监控,其他都是常见的监听生命周期的方式。。。。
记录完。
网友评论