说起leakcanary大家应该都很熟悉,问起原理应该都知道在对象被销毁时通过WeakReference+ReferenceQueue检测对象是否被回收,延迟二次检测后还没被回收则认为是嫌疑对象,然后dump heap并对其进行分析…

但是你知道leakcanary可以检测哪些对象吗?又是如何获取这些即将销毁的对象呢?

先说问题1的结论:

leakcanary2.6版本之前只能对Activity,Fragment进行监控。

leakcanary2.6版本以后增加了对ViewModel,RootView,Service的监控。

至于如何检测这些对象的销毁时机,下面以leakcanary-android:2.7代码为例做简单的探讨。

1,初始化

众所周知,leakcanary从2.0版本开始就不需要手动初始化了,其主要是通过ContentProvider来实现免初始化:

<application><providerandroid:name="leakcanary.internal.AppWatcherInstaller$MainProcess"android:authorities="${applicationId}.leakcanary-installer"android:enabled="@bool/leak_canary_watcher_auto_install"android:exported="false" />
</application>

在其onCreate()中进行了具体的初始化工作:

override fun onCreate(): Boolean {val application = context!!.applicationContext as ApplicationAppWatcher.manualInstall(application)return true
}

接着看AppWatcher.manualInstall()中做了什么:

fun manualInstall(application: Application,retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)) {...watchersToInstall.forEach {it.install()}
}

删繁就简,这里主要是遍历了watchersToInstall并调用了每个item的install(),那么watchersToInstall是什么呢?看它的默认实现appDefaultWatchers(application)

fun appDefaultWatchers(application: Application,reachabilityWatcher: ReachabilityWatcher = objectWatcher): List<InstallableWatcher> {return listOf(// 监控ActivityActivityWatcher(application, reachabilityWatcher),// 监控Fragment和ViewModelFragmentAndViewModelWatcher(application, reachabilityWatcher),// 监控RootViewRootViewWatcher(reachabilityWatcher),// 监控ServiceServiceWatcher(reachabilityWatcher))
}

这里就是返回了一个包含四个Watcher组成的List,分别对Activity,Fragment,ViewModel,RootView,Service的销毁进行监控,拿到即将销毁的对象通过WeakReference和ReferenceQueue方式进行内存泄漏的初步判断,最后Dump HeapProfile进行具体分析。

下面就看看这些Watcher是如何实现监控对象销毁过程的。

2,ActivityWatcher

ActivityWatcher非常简单,通过Application注册Activity的生命周期回调,来监控每一个Activity的销毁,在Activity销毁时通过reachabilityWatcher将当前Activity对象添加到监控队列,然后进行具体分析。

class ActivityWatcher(private val application: Application,private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {private val lifecycleCallbacks =object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityDestroyed(activity: Activity) {// 监控activity对象reachabilityWatcher.expectWeaklyReachable(activity, "${activity::class.java.name} received Activity#onDestroy() callback")}}override fun install() {// 注册Activity的生命周期回调application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}
}

3,FragmentAndViewModelWatcher

这个Watcher实现了对Fragment和ViewModel的销毁监控,首先看一下对Fragment的销毁监控:

3.1 监控Fragment销毁

Fragment有三种:framework自带的,supportv4包中的和androidx中的。因此需要对这三种情况分别处理,不过思路都是一样的,差别就在于导包。那么就看一下framework自带的Fragment如何监控。

class FragmentAndViewModelWatcher(private val application: Application,private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()// 添加对三种Fragment处理的Watcherif (SDK_INT >= O) {fragmentDestroyWatchers.add(AndroidOFragmentDestroyWatcher(reachabilityWatcher))}...fragmentDestroyWatchers}private val lifecycleCallbacks =object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityCreated(activity: Activity,savedInstanceState: Bundle?) {// 在ActivityCreate时调用fragmentDestroyWatchers中的每个Watcherfor (watcher in fragmentDestroyWatchers) {watcher(activity)}}}override fun install() {// 注册Activity的生命周期application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}}

接着看一下AndroidOFragmentDestroyWatcher中如何处理framework自带的Fragment。

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.viewif (view != null) {// 将Fragment中的view加入监控队列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) {// 将Fragment加入监控队列reachabilityWatcher.expectWeaklyReachable(fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback")}}override fun invoke(activity: Activity) {val fragmentManager = activity.fragmentManager// 通过fragmentManager注册Fragment的生命周期回调fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)}
}

使用的高阶函数,直接看invoke(),在其中获取Activity的fragmentManager,并注册Fragment的生命周期回调。

在回调onFragmentViewDestroyed中获取Fragment中的view,将view加入监控队列。

在回调onFragmentDestroyed中将Fragment加入监控队列。

3.2 监控ViewModel销毁

在对AndroidX中Fragment监控时实现了对ViewModel的监控,因为只有AndroidX中才提供了ViewModel。代码如下:

internal class AndroidXFragmentDestroyWatcher(private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {override fun onFragmentCreated(fm: FragmentManager,fragment: Fragment,savedInstanceState: Bundle?) {// 将Fragment中的ViewModel加入监控队列ViewModelClearedWatcher.install(fragment, reachabilityWatcher)}...}override fun invoke(activity: Activity) {if (activity is FragmentActivity) {val supportFragmentManager = activity.supportFragmentManager// 注册Fragment生命周期回调supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)// 将activity中的ViewModel加入监控队列ViewModelClearedWatcher.install(activity, reachabilityWatcher)}}
}

ViewModel存在于Activity和Fragment中,因此需要对两者的ViewModel进行监控,首先在invoke()中注册了Fragment生命周期回调,并在回调的onFragmentCreated通过ViewModelClearedWatcher对该Fragment中的ViewModel进行监控,然后直接通过ViewModelClearedWatcher对当前Activity进行了监控。

接着看一下ViewModelClearedWatcher是如何处理的:

internal class ViewModelClearedWatcher(storeOwner: ViewModelStoreOwner,private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {private val viewModelMap: Map<String, ViewModel>?init {// We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,// however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0// does not have ViewModelStore#keys. All versions currently have the mMap field.viewModelMap = try {val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")mMapField.isAccessible = true// 反射获取ViewModelStore实例中的mMap对象mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>} catch (ignored: Exception) {null}}override fun onCleared() {// 遍历mMap对象将其中的每个ViewModel对象加入监控队列viewModelMap?.values?.forEach { viewModel ->reachabilityWatcher.expectWeaklyReachable(viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback")}}companion object {fun install(storeOwner: ViewModelStoreOwner,reachabilityWatcher: ReachabilityWatcher) {// 创建ViewModelProvider并设置一个Factory,val provider = ViewModelProvider(storeOwner, object : Factory {override fun <T : ViewModel?> create(modelClass: Class<T>): T =// 创建ViewModelClearedWatcher并传递参数ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T})provider.get(ViewModelClearedWatcher::class.java)}}
}

注意ViewModelClearedWatcher是一个ViewModel,在install()中创建了ViewModelClearedWatcher的实例,在其初始化时反射获取当前ViewModelStoreOwner对象的用于保存ViewModel的mMap对象,然后在其onCleared时遍历mMap将其中的每个ViewModel对象加入监控队列。

3.3 小结:

1,对于Fragment的监控分了三种情况,分别是framework自带的,supportv4包中的和androidx中的。

2,Fragment的监控是在Activity创建时,获取当前Activity的fragmentManager,通过fragmentManager添加Fragment的生命周期回调,在回调中分别将Fragment对象以及其中的View添加到监控队列。

3,对于ViewModel的监控需要对Activity和Fragment中的ViewModel分别进行。

4,通过当前ViewModelStoreOwner实例创建ViewModel对象,则该ViewModel对象会跟随ViewModelStoreOwner实例一起销毁。

5,通过反射获取当前ViewModelStoreOwner中用于存放ViewModel的集合mMap,在4中创建的ViewModel销毁时遍历该mMap,将其每个对象都添加到监控队列中。

4,RootViewWatcher

RootViewWatcher监测的是DecorView,在Activity,Dialog,ToolTip和Toast等创建过程中都涉及到DecorView的创建,那怎么获取到这玩意的添加和销毁呢?

熟悉View相关流程的应该知道,在ActivityThread中执行完Activity的onResume后会将其DecorView添加到WindowManagerGlobal的一个集合中,可以通过反射获取到这个集合,对这个集合进行代理即可监听DecorView的添加和删除,添加DecorViewAttachStateChangeListener即可监听DecorView的AttachedDetached状态。

LeakCanary的代码过于复杂,下面用简单的代码实现其大体流程:

class RootViewSpy {fun install() {val windowManagerClass = Class.forName("android.view.WindowManagerGlobal")// 1,反射获取WindowManagerGlobal实例对象val windowManagerInstance = windowManagerClass.getMethod("getInstance").invoke(null)// 2,反射获取WindowManagerGlobal实例对象中的mViews集合val mViewsField =windowManagerClass.getDeclaredField("mViews").apply { isAccessible = true }val mViews = mViewsField.get(windowManagerInstance) as ArrayList<View>// 3,将mViews中的内容存入代理集合中delegatingViewList.apply { addAll(mViews) }// 4,用代理集合替换原始mViews对象mViewsField.set(windowManagerInstance, delegatingViewList)}// 代理对象private val delegatingViewList = object : ArrayList<View>() {override fun add(rootView: View): Boolean {// 给DecorView添加AttachStateChange状态监听rootView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {override fun onViewAttachedToWindow(v: View) {LogUtil.e("onViewAttachedToWindow")}override fun onViewDetachedFromWindow(v: View) {LogUtil.e("onViewDetachedFromWindow")}})return super.add(rootView)}}
}

对WindowManagerImpl进行动态代理也可以监听DecorView的添加和删除

小结

1,反射获取WindowManagerGlobal中用于存DecorView的集合

2,对这个集合进行代理,监控DecorView的添加过程

3,对每个DecorView添加AttachStateChangeListener,监测其AttachedDetached过程

4,由于前面已经有了Activity的检测,RootViewWatcher主要会对Toast,ToolTip及Dialog(默认不检测)的RootView进行监测

5,ServiceWatcher

监控服务的销毁需要对Service的Stop流程有所了解,Service的Stop有三种情况:Activity中stopService(),Service中stopSelf()和unBindService()。这三种情况通过不同方式进入AMS,但最终都会通过Binder方式调用ApplicationThread的scheduleStopService()方法,ApplicationThread会通过handler发送消息给ActivityThread;ActivityThread中执行Service的Stop相关逻辑,最后ActivityThread会通知AMS做最后收尾工作。大体流程如下:

要监听Service的Stop有两个点:

  1. 在ActivityThread收到ApplicationThread消息时也就是上图中的Hook点1,通过给ActivityThread中的Handler对象添加callback来实现,此时Service并没有开始执行stop相关操作,因此可以获取其实例。

  2. 在ActivityThread中执行完Service的Stop后会通过binder调用通知AMS完成最后的工作,可以通过Hook AMS来监听到。此时服务已经执行了onDestory(),可能无法在获取到其实例了,因此需要在hook点1处保存service实例,然后在此处获取实例。

LeakCanary在Hook点1处获取即将stop的Service的实例,并通过弱引用保存,然后在Hook点2处获取实例的弱引用,进而对其监听。

代码使用了高阶函数,大体如下:

class ServiceWatcher {private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }// 反射获取ActivityThread实例private val activityThreadInstance by lazy {activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!}// 获取ActivityThread中的mService对象private val activityThreadServices by lazy {val mServicesField =activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }mServicesField.get(activityThreadInstance) as Map<IBinder, Service>}fun install() {try {swapActivityThreadHandlerCallback { mCallback ->// 创建一个Handler的Callback对象Handler.Callback { msg ->if (msg.what == STOP_SERVICE) {val key = msg.obj as IBinder// 根据key(token)从mService中获取Service对象activityThreadServices[key]?.let {onServicePreDestroy(key, it)}}mCallback?.handleMessage(msg) ?: false}}swapActivityManager { activityManagerInterface, activityManagerInstance ->// 动态代理Proxy.newProxyInstance(activityManagerInterface.classLoader, arrayOf(activityManagerInterface)) { _, method, args ->if (METHOD_SERVICE_DONE_EXECUTING == method.name) {val token = args!![0] as IBinderif (servicesToBeDestroyed.containsKey(token)) {// 当调用AMS的serviceDoneExecuting方法时执行onServiceDestroyed()onServiceDestroyed(token)}}try {if (args == null) {method.invoke(activityManagerInstance)} else {method.invoke(activityManagerInstance, *args)}} catch (invocationException: InvocationTargetException) {throw invocationException.targetException}}}} catch (ignored: Throwable) {LogUtil.e("Could not watch destroyed services")}}private fun onServicePreDestroy(token: IBinder,service: Service) {LogUtil.e("onServicePreDestroy")servicesToBeDestroyed[token] = WeakReference(service)}private fun onServiceDestroyed(token: IBinder) {servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->serviceWeakReference.get()?.let { service ->LogUtil.e("${service::class.java.name} received Service#onDestroy() callback")}}}/*** hook ActivityThread的handler*/private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {val mHField =activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }val mH = mHField[activityThreadInstance] as Handlerval mCallbackField =Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }// 获取mH的mCallbackval mCallback = mCallbackField[mH] as Handler.Callback?// 替换mH的mCallback对象mCallbackField[mH] = swap(mCallback)}/*** hook ams binder proxy*/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.// 获取AMS的binder代理对象val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)val iActivityManagerInterface = Class.forName("android.app.IActivityManager")// 用动态代理对象替换原对象mInstanceField[activityManagerSingletonInstance] =swap(iActivityManagerInterface, activityManagerInstance!!)}companion object {private const val STOP_SERVICE = 116private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"}
}

有一点需要注意,两个Hook点的方法参数都是IBinder类型的token,那如何根据token获取其实例呢?查看ActivityThread代码:

public final class ActivityThread {...// 保存所有Service实例及其对应IBinderfinal ArrayMap<IBinder, Service> mServices = new ArrayMap<>();...// ActivityThread的当前实例private static ActivityThread sCurrentActivityThread;...
}

其中mServices保存所有Service实例及其对应IBinder,sCurrentActivityThread是ActivityThread的当前实例并且是静态的,因此可以直接反射获取sCurrentActivityThread实例,然后可以反射获取mServices对象,根据Hook点的token参数即可获取Service实例。

小结
  1. 反射获取ActivityThread中的mServices实例,方便后面获取每个Service的实例
  2. 获取ActivityThread中的handler,给这个handler添加一个callback,即可实现对这个handler的hook
  3. 在第2步中,通过token参数从mServices中获取即将stop的Service的实例,然后通过弱引用保存
  4. Hook AMS,在ActivityThread执行完Service的stop操作后通知AMS时从2中的弱引用集合中获取Service的引用,将其加入监控队列
  5. 关于Hook AMS过程这里不做详细介绍,可以参考https://juejin.cn/post/7006951885089292296

6,最后

以上已经获取到了检测对象销毁的时机,接下来就是判断这些被销毁的对象是否发生了泄漏。至于如何dump heap,如何分析,如何通知用户等不是本文的重点,相关的文章也挺多的,这里不再具体分析。

LeakCanary虽然是傻瓜式的工具,可以很方便的帮我们检测内存泄漏问题,但是要想使用好他就要知道它可以检测哪些对象的泄漏。

大家都觉得反射、hook是洪水猛兽,但LeakCanary中却各种反射、各种hook,了解了这些思路方案对于解决一些特殊问题还是很有帮助的。

LeakCanary可能被你忽略的点相关推荐

  1. 使用LeakCanary遇到的问题 就是不弹出来

    今天楼主遇到引用LeakCanary时代码跟官网一样但是就不弹出来.楼主新建项目就可以正常使用.楼主郁闷半天,现在终于整出来了. 楼主主工程app引用module为thirdParty,本想为了整洁三 ...

  2. LeakCanary 源码分析

    1. 前言 LeakCanary 是由 Square 开发的一款内存泄露检测工具.相比与用 IDE dump memory 的繁琐,它以轻便的日志被广大开发者所喜爱.让我们看看它是如何实现的吧. ps ...

  3. Android内存泄漏检测利器:LeakCanary

    2019独角兽企业重金招聘Python工程师标准>>> 是什么? 一言以蔽之:LeakCanary是一个傻瓜化并且可视化的内存泄露分析工具 为什么需要LeakCanary? 因为它简 ...

  4. leaks Android内存泄露,Android LeakCanary 检测内存泄露

    内存泄漏: 指程序在申请内存后 ,无法释放已经申请的内存空间,一次内存泄漏可以忽略,但内存泄漏堆积后果很严重,无论多少内存,都会被占光 内存泄露危害: 1.内存泄露最终会导致内存溢出(OOM) 2.导 ...

  5. 一步步拆解 LeakCanary

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 java 源码系列 - 带你读懂 Reference 和 ReferenceQueue https://blog.csdn.net/ ...

  6. LeakCanary是如何定位内存泄漏的,看完就懂了

    文章目录 一.LeakCanary的简单使用 二.LeakCanary原理简单分析: 2-1.LeakCanary原理简述 2-2.ActivityLifecycleCallbacks使用 2-2-1 ...

  7. 内存泄漏分析框架LeakCanary的使用与原理解析

    文章目录 1. 常见内存泄漏 1.1 "单例模式" 造成的内存泄漏 1.2 "静态实例" 造成内存泄漏 1.3 "Handler" 造成的内 ...

  8. leakCanary原理

    一.面试中的问题 一般的中高级面试,都会问到性能优化,内存优化问题,而说到内存问题就肯定会问到内存泄漏问题,而一般的求职者二话不说,直接就上LeakCanary, 紧接着肯定是问:那你知道LeakCa ...

  9. LeakCanary原理解析

    简介 LeakCanary是一款开源的内存泄漏检查工具,在项目中,可以使用它来检测Activity是否能够被GC及时回收. 使用方式解析 LeakCanary.install()方法的调用流程如下所示 ...

最新文章

  1. C++中const指针用法汇总
  2. 技术玩法大升级,网易MCtalk揭秘社交产品背后的秘密
  3. 如何启用nodejs request模块的调试模式
  4. 剑灵力士卡刀ahk_技术宅教你:召唤代码一键卡刀详细教程帖
  5. 查询成绩最好的前两名_一级建造师成绩查询前这几件事很重要
  6. 批量 材质 调整_游戏图形批量渲染及优化:Unity静态合批技术
  7. oracle 创建用户
  8. Kotlin入门(23)适配器的进阶表达
  9. 摄影测量学之共线方程的应用
  10. SSDP:DDoS***的“新宠”
  11. python游戏编程讲解之凯撒密码
  12. sqlmap安装总结
  13. Ps雅点设计合成大师
  14. 渗透之——ASP Web提权
  15. JAVA代码翻译更新(第五篇)
  16. Linux mmc驱动框架(4)——卡检测及初始化
  17. (大数据应用考察)全国水资源分析可视化
  18. 淘宝购物车5年技术升级与沉淀
  19. 前端内容安全策略(csp)
  20. 前端学习个人心得,总结(个人向)

热门文章

  1. HTML5页面被运营商DNS劫持问题及解决方案,app中h5页面源码的获取
  2. 即将迎来5周岁,谷歌把lstio捐赠给CNCF
  3. GPU服务器中挖矿病毒-查杀-分析-预防
  4. excel怎么把竖排变成横排_预算在6000元!这四款手机怎么选?
  5. Java面试复习总结(Mysql篇9)——MySQL里的那些日志
  6. 2022快手电商短视频运营白皮书:Q2对比Q1GMV总值增长率达12%
  7. 一台主机几个服务器系统吗,一台主机几个服务器系统吗
  8. 【唐迟阅读】考研英语真题题型分类,话题划分
  9. 手势识别控制鼠标和键盘
  10. 聊聊学历那点事----学历重要还是能力重要?