LeakCanary2源码分析
LeakCanary介绍
A memory leak detection library for Android.
LeakCanary是Android上用于检查内存泄漏的工具,LeakCanary大大减少因内存泄漏导致的内存溢出(OutOfMemoryError)奔溃,LeakCanary的易用和有效让它成为我最喜欢的开源框架之一,下面我们会先简单介绍其用法,然后深入了解LeakCanary检查内存泄漏的原理。
LeakCanary使用
加入依赖
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
}
只需要添加相关依赖,便可集成,非常方便,如果从1.6版本升级,可以参考《Upgrading to LeakCanary 2》进行升级。
安装并启动APP,看到下面这个log,说明已经添加成功:
D/LeakCanary: Installing AppWatcher
简单实践
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.d("MainActivity","Run");
}
}, 10000);
}
}
这里我们写了个10s的延时任务,打开APP后迅速按返回键退出,这个时候LeakCanary有如下提示:
从LeakCanary
的提示我们可以知道,MainActivity
发生了内存泄漏,而泄漏的原因是MessageQueue
持有了MainActivity
,导致MainActivity
无法正常退出,这里只是简单举例,大家可以加到自己项目中,相信会有不少收获。
LeakCanary源码分析
本文使用的是LeakCanary最新的2.2版本
从1.6.3开始,LeakCanary
就使用Kotlin重写了一次,代码量(不包括空格和注释)从Java时期的6000增加到Kotlin
版本的16000行,代码增加的原因是LeakCanary
不在依赖HAHA
(perflib
的重新打包),而是重新开发了自己的堆转储解析器:Shark
,Shark
的使用提升了速度并减少了内存使用。额外代码主要来自Shark, 自动化测试和权限相关的UI代码。
下面我们将开始分析全新的,Kotlin
版本的LeakCnary
:
1. 如何查看源码
因为我们使用的2.2版本,没有显式的框架入口,无法使用AndroidStudio的关联功能,所有我们要自己手动找代码,那么如何找到LeakCanary相关代码呢,我们需要到Lib里面找:
-
在Project Pannel中选择 “Project”
-
点击"External Libraries"
-
下拉找到LeakCanary的包,展开就可以看到相关代码
我们可以看到LeakCanary的不少包,主要包括:
-
leakcanary-android
集成入口模块,提供 LeakCanary 安装,公开 API 等能力
-
leakcanary-android-process
和 leakcanary-android 一样,区别是会在单独的进程进行分析
-
leakcanary-android-core
核心模块
-
leakcanary-object-watcher-android,leakcanary-object-watcher-android-androidx,leakcanary-watcher-android-support-fragments
对象实例观察模块,在
Activity
,Fragment
等对象的生命周期中,注册对指定对象实例的观察,有Activity
,Fragment
,Fragment View
,ViewModel
等 -
shark-android
提供特定于 Android 平台的分析能力。例如设备的信息,Android 版本,已知的内存泄露问题等
-
shark
hprof 文件解析与分析的入口模块
-
shark-graph
分析堆中对象的关系图模块
-
shark-hprof
解析 hprof 文件模块
-
shark-log
日志模块
2. 源码分析
LeakCanary 2.0
不需要添加代码便可以跟随APP启动,原理在于利用了ContentProvider
的特性,ContentProvider.onCreate
方法会先于Application.onCreate
执行。具体的实现只需要在AndroidManifeat.xml中配置一下定制的ContentProvider
,在其onCreate
方法中通过install进行初始化初始化,便可以省去LeakCanary 1.6
版本中在Application中install
的步骤:
//注册ContentProvider @leakcanary-object-watcher-android/src/main/AndroidManifest.xml
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:exported="false"/>
</application>
首先在AndroidManifest.xml
中注册contentProvider
,然后对contentProvider
进行定义:
//@AppWatcherInstaller.kt
internal class LeakCanaryProcess : AppWatcherInstaller() {
override fun onCreate(): Boolean {
super.onCreate()
AppWatcher.config = AppWatcher.config.copy(enabled = false)
return true
}
}
override fun onCreate(): Boolean {
//获取application
val application = context!!.applicationContext as Application
//-->2.1 加载LeakCanary
InternalAppWatcher.install(application)
return true
}
}
//2.1 加载LeakCanary @InternalAppWatcher.kt
fun install(application: Application) {
...
//检查当前线程是否有主线程
checkMainThread()
if (this::application.isInitialized) {
//如果LeakCanary已经加载过,直接放回
return
}
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
//-->2.1监视Activity
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.2监视Fragment
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.3调用上层模块InternalLeakCanary.invoke
onAppWatcherInstalled(application)
}
2.1 ActivityDestroyWatcher.install
// @ActivityDestroyWatcher.kt
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
//-->2.1.1 创建Activity destroy监听回调
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
//-->2.1.2 同Application绑定
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
//2.1.1 创建Activity destroy监听回调
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
//Activity destroy触发存在对象检查
if (configProvider().watchActivities) {
// -->2.1.2 objectWatcher监视activity
objectWatcher.watch