文章目录
概述
LeakCanary是Android开发中常用的用来检测内存泄漏的框架,它能够帮助开发者快速发现是否发生内存泄漏,并可视化的给予提示。
这里基于LeakCanary 2.3版本进行分析。
只需要在工程中进行简单的集成配置,就能自动对Activity、Fragment、fragment View进行自动检测。也可以通过ObjectWatcher#watch方法对指定对象进行检测。
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
}
仅需添加一行依赖,就完成了接入工作,当buildTypes为debug时,运行APP就会自动启动LeakCanary。
核心流程拆解
LeakCanary的核心功能,大致可以分为两块:
- 检查对象是否被释放,若未被及时回收,则可能发生内存泄漏
- dump heap并解析hprof文件,查找检测的对象生成引用链信息
检测泄漏功能的主要流程如下:
-
SDK自初始化
在1.X是通过手动调用方法初始化,2.X实现自动初始化。 -
自动对Android特定对象进行检测
例如自动监测Activity、Fragment、fragment View生命周期销毁。 -
检测判断对象是否发生内存泄漏
检查一个对象应该被回收,可能产生内存泄漏而没有被回收。 -
dump heap至hprof文件并解析文件,生成泄漏引用链
依赖另一个专门分析hprof的库来解析文件和生成分析结果。在1.X是依赖haha库,2.X改成依赖shark库。 -
在通知栏和Activity界面显示泄漏信息
把泄漏分析结果发送到通知栏和Activity界面中显示可能导致泄漏的对象引用链。
源码探究
SDK自动初始化
前面看到LeakCanary只需要添加一行依赖,不需要开发者手动调用它的方法,那么它是如何自己把自己启动起来呢?可以猜测通过注册静态广播监听系统广播事件或者注册ContentProvider。
查找LeakCanary开源库中的AndroidManifest.xml,可以发现没有符合的静态注册广播,而存在ContentProvider:
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:exported="false"/>
在AppWatcherInstaller中会进行LeakCanary的初始化。
关于ContentProvider初始化
清单中注册的ContentProvider在应用启动的时候就会进行初始化:
[ActivityThread#handleBindApplication]
private void handleBindApplication(AppBindData data) {
// ···
Application app;
// ···
// 反射实例化Application,并会调用其attachBaseContext方法
app = data.info.makeApplication(data.restrictedBackupMode, null);
// ···
// 初始化注册的ContentProvider
installContentProviders(app, data.providers);
// ···
// 触发Application的onCreate回调
mInstrumentation.callApplicationOnCreate(app);
// ···
}
ContentProvider启动后就会回调onCreate方法。
AppWatcherInstaller
AppWatcherInstaller继承ContentProvider,在其onCreate回调中,调用InternalAppWatcher#install方法进行初始化。
InternalAppWatcher
InternalAppWatcher负责SDK的初始化。
// 初始化ObjectWatcher,用于检测传入的对象是否未被及时回收
val objectWatcher = ObjectWatcher(
clock = clock,
checkRetainedExecutor = checkRetainedExecutor,
isEnabled = {
AppWatcher.config.enabled }
)
接着看它的init代码块:
init {
// 通过反射获取InternalLeakCanary实例
val internalLeakCanary = try {
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null)
} catch (ignored: Throwable) {
NoLeakCanary
}
@kotlin.Suppress("UNCHECKED_CAST")
// onAppWatcherInstalled指向InternalLeakCanary#invoke(Application)方法
onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
}
接着看install方法:
fun install(application: Application) {
SharkLog.logger = DefaultCanaryLog()
// 检查是否在主线程,不在主线程则抛异常
checkMainThread()
if (this::application.isInitialized) {
return
}
InternalAppWatcher.application = application
val configProvider = {
AppWatcher.config }
// Activity自动监测相关初始化
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
// Fragment自动监测相关初始化
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
// 在init代码块中,onAppWatcherInstalled被赋值为InternalLeakCanary#invoke
onAppWatcherInstalled(application)
}
该方法中进行了Activity和Fragment自动监测功能的初始化,最后初始化InternalLeakCanary。
接着看InternalLeakCanary的invoke方法:
override fun invoke(application: Application) {
this.application = application
// 检查是否在DEBUGGABLE启动,若不是则抛异常(除非开启设置)
checkRunningInDebuggableBuild()
// 添加OnObjectRetainedListener监听,单发生对象未及时回收时,会回调onObjectRetained
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
// 用于dump heap
val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
// 用于主动触发gc
val gcTrigger = GcTrigger.Default
val configProvider = {
LeakCanary.config }
// 开辟子线程,创建子线程通信handler
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
// 用于调度dump heap
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
configProvider
)