LeakCanary 2.4 Kotlin源码
1. 初始化
新的LeakCanary使用kotlin开发,而且不需要在Application中添加LeakCanary.install(this)进行初始化。那么究竟是用什么方式能够跟随APP启动进行自动初始化的呢?
在leakcanary-object-watcher-android module的 manifest中看到了:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.squareup.leakcanary.objectwatcher"
>
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false"/>
</application>
</manifest>
而我们转到源码查看AppWatcherInstaller$MainProcess类
/**
* Content providers are loaded before the application class is created. [AppWatcherInstaller] is
* used to install [leakcanary.AppWatcher] on application start.
*/
internal sealed class AppWatcherInstaller : ContentProvider() {
/**
* [MainProcess] automatically sets up the LeakCanary code that runs in the main app process.
*/
internal class MainProcess : AppWatcherInstaller()
/**
* When using the `leakcanary-android-process` artifact instead of `leakcanary-android`,
* [LeakCanaryProcess] automatically sets up the LeakCanary code
*/
internal class LeakCanaryProcess : AppWatcherInstaller()
//...
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
}
AppWatchInstaller是一个继承了ContentProvider的密封类。关于密封类的解释:www.kotlincn.net/docs/refere… 。我理解就像是个可以有多个不同实例的枚举类型。MainProcess和LeakCanaryProcess两个子类分别用于不同的进程中启动。
AppWatchInstaller中实现的ContentProvider的query, getType, insert, delete, update等方法都只是个空实现,返回null。很明显这个ContentProvider是个不务正业的数据提供者,他不是为了提供数据,而是要在APP启动时,执行AppWatcher.manualInstall(application)
AppWather调用InternalAppWatcher.install(application)
fun manualInstall(application: Application) {
InternalAppWatcher.install(application)
}
InternalAppWatcher:
fun install(application: Application) {
checkMainThread()
if (this::application.isInitialized) {
return
}
SharkLog.logger = DefaultCanaryLog()
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
onAppWatcherInstalled(application)
}
- 检查是否在主线程
- 检查application是否已经赋值
- ActivityDestroyWatcher.install
- FragmentDestroyWatcher.install
- install完成后要做的的动作
ActivityDestroyWatcher.install的方法在ActivityDestroyWatcher的companion object中,相当于Java中的静态方法
internal class ActivityDestroyWatcher private constructor(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
if (configProvider().watchActivities) {
objectWatcher.watch(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
}
在install方法中通过application.registerActivityLifecycleCallbacks()注册了Activity生命周期的监听,调用ObjectWatcher.watch()方法开始监测Activity。
FragmentDestroyWatcher是一个internal object。internal 表示可见范围是module内,object是对象声明,其实就是一个单例。代码如下:
internal object FragmentDestroyWatcher {
private const val ANDROIDX_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"
private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidXFragmentDestroyWatcher"
// Using a string builder to prevent Jetifier from changing this string to Android X Fragment
@Suppress("VariableNaming", "PropertyName")
private val ANDROID_SUPPORT_FRAGMENT_CLASS_NAME =
StringBuilder("android.").append("support.v4.app.Fragment")
.toString()
private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidSupportFragmentDestroyWatcher"
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> AppWatcher.Config
) {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
)
}
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}
if (fragmentDestroyWatchers.size == 0) {
return
}
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
})
}
private fun getWatcherIfAvailable(
fragmentClassName: String,
watcherClassName: String,
objectWatcher: ObjectWatcher,
configProvider: () -> AppWatcher.Config
): ((Activity) -> Unit)? {
return if (classAvailable(fragmentClassName) &&
classAvailable(watcherClassName)
) {
val watcherConstructor = Class.forName(watcherClassName)
.getDeclaredConstructor(ObjectWatcher::class.java, Function0::class.java)
@Suppress("UNCHECKED_CAST")
watcherConstructor.newInstance(objectWatcher, configProvider) as (Activity) -> Unit
} else {
null
}
}
private fun classAvailable(className: String): Boolean {
return try {
Class.forName(className)
true
} catch (e: Throwable) {
// e is typically expected to be a ClassNotFoundException
// Unfortunately, prior to version 25.0.2 of the support library the
// FragmentManager.FragmentLifecycleCallbacks class was a non static inner class.
// Our AndroidSupportFragmentDestroyWatcher class is compiled against the static version of
// the FragmentManager.FragmentLifecycleCallbacks class, leading to the
// AndroidSupportFragmentDestroyWatcher class being rejected and a NoClassDefFoundError being
// thrown here. So we're just covering our butts here and catching everything, and assuming
// any throwable means "can't use this". See https://github.com/square/leakcanary/issues/1662
false
}
}
}
如果系统是Android O以后版本,使用AndroidOFragmentDestroyWatcher,如果app使用的是androidx中的fragment,则添加对应的AndroidXFragmentDestroyWatcher,如果使用support库中的fragment,则添加AndroidSupportFragmentDestroyWatcher。
然后注册Activity生命周期监听,在onActivityCreated时开始监听Activity的fragment。
这里的xxWatcher都继承(Activity) -> Unit,啥意思呢?这些Watcher是 方法类型!就是说他们是个方法,方法参数为Activity类型,没有返回值。所以他们都要重写invoke方法。下面是AndroidOFragmentDestroyWatcher的invoke方法:
override fun invoke(activity: Activity) {
val fragmentManager = activity.fragmentManager
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
}
其他几个FragmentDestroyWatcher的invoke()方法都差不多,使用FragmentManager或SupportFragmentManager.registerFragmentLifecycleCallbacks()注册监听。不过具体监听方式有所不同。稍后再讲。
Activity和Fragment对应的watcher 在 install之后就是onAppWatcherInstalled(application)
onAppWatcherInstalled也是个方法类型,参数为Application,没有返回值。
InternalAppWatcher中的初始化代码块就是onAppWatcherInstalled赋值的地方。这个初始化代码块相当于Java中静态类中的static代码块。
init {
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 as (Application) -> Unit
}
这里onAppWatcherInstalled赋值到InternalLeakCanary对象。我们来到InternalLeakCanary的invoke方法:
override fun invoke(application: Application) {
_application = application
checkRunningInDebuggableBuild()
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))
val gcTrigger = GcTrigger.Default
val configProvider = { LeakCanary.config }
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
configProvider
)
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
registerResumedActivityListener(application)
addDynamicShortcut(application)
disableDumpHeapInTests()
}
- heapDumper 用于确认内存泄漏之后进行 heap dump 工作。
- gcTrigger 用于发现可能的内存泄漏之后手动调用 GC 确认是否真的为内存泄露。
这两个对象是 LeakCanary 检测内存泄漏的核心。后面会进行详细分析。到这里,整个 LeakCanary 的初始化工作就完成了。
2. ObjectWatcher.watch()
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
private val queue = ReferenceQueue<Any>()
@Synchronized fun watch(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
SharkLog.d {
"Watching " +
(if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
(if (description.isNotEmpty()) " ($description)" else "") +
" with key $key"
}
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
- 调用removeWeaklyReachableObjects(),移除watchedObjects中将被GC的引用。
- 将要观察的对象生成KeyedWeakReference并加入到watchedObjects中
- 使用checkRetainedExecutor定时执行moveToRetained()
调用removeWeaklyReachableObjects方法,多次从queue中取出已回收的对象,并从watchedObject中删除
private fun removeWeaklyReachableObjects() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
moveToRetained(key: String) 方法,再次移除watchedObjects中将被GC的引用,然后取出未被回收的retainedRef,执行onObjectRetainedListeners.onObjectRetained()
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
onObjectRetainedListeners.onObjectRetained()最后会回到InternalLeakCanary.onObjectRetained(),并且在里面调用HeapDumpTrigger.onObjectRetained(),然后是
private fun scheduleRetainedObjectCheck(
reason: String,
rescheduling: Boolean,
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
if (checkCurrentlyScheduledAt > 0) {
val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" }
return
} else {
val verb = if (rescheduling) "Rescheduling" else "Scheduling"
val delay = if (delayMillis > 0) " in ${delayMillis}ms" else ""
SharkLog.d { "$verb check for retained objects${delay} because $reason" }
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects(reason)
}, delayMillis)
}
最后就是通过checkRetainedObjects(reason)检查是否泄漏
private fun checkRetainedObjects(reason: String) {
val config = configProvider()
// A tick will be rescheduled when this is turned back on.
if (!config.dumpHeap) {
SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
return
}
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
// 泄漏对象数小于5个,不进行dump,只给弹通知。
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
// 如果正在debug,不进行dump,因为debug可能会引起临时的内存泄漏(比如被断点block的线程)
if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
onRetainInstanceListener.onEvent(DebuggerIsAttached)
// 显示相关通知和提示
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_debugger_attached
)
)
// 等待20秒后重新检查
scheduleRetainedObjectCheck(
reason = "debugger is attached",
rescheduling = true,
delayMillis = WAIT_FOR_DEBUG_MILLIS
)
return
}
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
// 距上次dump小于60秒,等待够了60秒后再检查
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
onRetainInstanceListener.onEvent(DumpHappenedRecently)
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
)
scheduleRetainedObjectCheck(
reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
rescheduling = true,
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
dismissRetainedCountNotification()
dumpHeap(retainedReferenceCount, retry = true)
}
在经过一系列的条件判定之后,进行dumpHeap。
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean
) {
// 资源id和name对应起来
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
// dump文件
val heapDumpFile = heapDumper.dumpHeap()
if (heapDumpFile == null) {
if (retry) {
SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
scheduleRetainedObjectCheck(
reason = "failed to dump heap",
rescheduling = true,
delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
)
} else {
SharkLog.d { "Failed to dump heap, will not automatically retry" }
}
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_dump_failed
)
)
return
}
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
// 分析
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}
HeapAnalyzerService.runAnalysis(application, heapDumpFile)启动一个前台服务来进行dump文件分析。现在不再使用haha的库来解析,而是改用自己的一套shark库解析。具体内容见链接:square.github.io/leakcanary/…
新的LeakCanary主要有以下不同
- 百分之百使用 Kotlin 重写
- 自动初始化,无需用户手动再添加初始化代码
- 支持Fragment和androidx
- 当泄露引用到达 5 个时才会发起 heap dump
- 全新的内存解析方法,减少 90% 内存占用,提升 6 倍速度
首次源码解析,有的地方可能理解错误,还请多多指正!