leakcanary 2.0相比1.6来说多了些骚操作,比如install不需要手动调了。另外代码也切到kotlin了,同时可以监控support包下fragment的内存泄露了。
不用手动install了?
相比于之前的1.6的版本,leakcanary的引入只需要在build.gradle中加:
1 | dependencies { |
而不需要再手动调用初始化代码了。原先因为不同进程会有各自的application对象,所以在application的onCreate中需要判断进程然后手动调用leakcanary.install把application对象传入,然后利用application给activity注册lifecycle callbacks来监听activity的onDestroyed。现在为啥不用手动install了?
1 | ➜ leakcanary git:(master) tree -L 1 |
首先看下leakcanary源码层级,在leakcanary-leaksentry中我们找到了:
1 | /** |
可看到是LeakSentryInstaller这个ContentProvider的onCreate里执行了InternalLeakSentry.install(application)
,我们知道 ContentProvider的onCreate是在application onCreate之前调的 ,所以才省掉了手动install的操作。具体可以从源码验证:
[ActivityThread.main()]
1 | public static void main(String[] args) { |
接着调到了AMS的attachApplication
:
1 |
|
接着调到了IApplicationThread
这个aidl接口的bindApplication
,具体实现是ActivityThread的内部类ApplicationThread:
1 | public final void bindApplication(String processName, ApplicationInfo appInfo, |
一路调用到了installContentProviders
:
1 |
|
1 | public ContentProvider instantiateProvider(@NonNull ClassLoader cl, |
到这里已经把install的骚操作前因后果都讲清楚了。其实就是利用了ContentProvider的生命周期来代替我们做手动install操作。当然这样会拖慢应用的启动速度,但是因为leakcanary只在debug模式下引入,所以对启动速度的影响不是问题。
如何监控内存泄漏?
来看InternalLeakSentry
的install方法:
1 | fun install(application: Application) { |
其中activity的监控主要是通过application的registerActivityLifecycleCallbacks
注册了onDestroyed的监听
1 | internal class ActivityDestroyWatcher private constructor( |
在onDestroyed的时候调用了refWatcher.watch(activity)
1 |
|
可以看到首先是调了个removeWeaklyReachableInstances
把queue里的引用都从watchedInstances中移除了。其中queue中的对象都是不应该判断为内存泄漏的对象。而watchedInstances是存放着监控的对象。checkRetainedExecutor会在5s后执行moveToRetained(key)
,moveToRetained(key)
会将其引用的retainedUptimeMillis
赋值为当前时间,标记为泄漏对象。以及调用onInstanceRetained()
,最终调到InternalLeakCanary
:
1 | override fun onReferenceRetained() { |
一步步调到HeapDumpTrigger
的checkRetainedInstances
:
同理fragment的监控也是通过FragmentSupportManager
的registerFragmentLifecycleCallbacks
方法监听fragment destroy进行的。
// TODO 本文只是分析了下监控的原理,但是其中如何解析hprof文件然后生成内存泄漏的调用链这部分并没有涉及。