Android LeakCanary源码分析

文章目录

Android LeakCanary源码分析

概述

LeakCanary 2 是 Square 公司推出的用于 Android 应用自动检测内存泄漏 的开源库。

核心类

  • MainProcessAppWatcherInstaller:继承自ContentProvider类,是 LeakCanary 的初始化类
  • AppWatcher:全局监听器的管理入口,初始化所有的监听器
  • ActivityWatcher:Activity 监控器,用于监听 Activity 的内存泄露
  • ObjectWatcher:核心监听器类,全局单例,将对象包装为带 ReferenceQueue 的 WeakReference 对象,默认5秒后触发GC,检查未被回收的对象
  • KeyedWeakReference:继承自 WeakReference 类,内存持有被引用对象和引用队列

流程

  1. 初始化:在 MainProcessAppWatcherInstaller.onCreate() 中初始化,
    1. MainProcessAppWatcherInstaller 是一个 ContentProvider
    2. ContentProvider.onCreate() 在 Application.onCreate() 之前被系统调用
  2. 监控对象:
    1. AppWatcher.manualInstall():安装4种监控器
    2. 例如 ActivityWatcher,在安装时会绑定生命周期,在 Activity 销毁时会回调 onDestory(),触发内存监控,这时会将 Activity 对象包装成 KeyedWeakReference 对象,并将该对象放入观察集合(watchedObjects)中
  3. 检测泄露:
    1. 每次监控对象时,会先清理已回收对象,再延迟5秒检查,将所有监控对象交给 HeapDumpTrigger 处理
    2. 轮询 checkRetainedObjects() 检查泄露对象,每次调用都会主动触发一次 GC
    3. 是否生成 dump 为年,前台状态,>= 5个泄露对象才生成,后台状态,>=1个立即生成
  4. 生成内存快照:最终调用 Debug.dumpHprofData(heapDumpFile.absolutePath) 生成内存快照
  5. 分析泄露:使用 Shark 分析内存快照

源码分析

MainProcessAppWatcherInstaller.onCreate()
xml 复制代码
<application>
    <provider
        android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
        android:authorities="${applicationId}.leakcanary-installer"
        android:enabled="@bool/leak_canary_watcher_auto_install"
        android:exported="false" />
</application>
kotlin 复制代码
internal class MainProcessAppWatcherInstaller : ContentProvider() {

    override fun onCreate(): Boolean {
        val application = context!!.applicationContext as Application
        // 安装监控器
        AppWatcher.manualInstall(application)
        return true
    }
}

MainProcessAppWatcherInstaller 类是继承自 ContentProvider。

App 在启动时,系统会在 Application.onCreate() 之前调用所有注册的 ContentProvider.onCreate()。

接着调用 AppWatcher.manualInstall() 自动安装各种监听器。

AppWatcher.manualInstall()
kotlin 复制代码
@JvmOverloads
fun manualInstall(
    application: Application,
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 延迟5秒执行
    watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) // 安装所有监控器
) {
    // 检查主线程
    checkMainThread()
    // 防止重复安装
    if (isInstalled) {
        throw IllegalStateException(
            "AppWatcher already installed, see exception cause for prior install call", installCause
        )
    }
    this.retainedDelayMillis = retainedDelayMillis
    if (application.isDebuggableBuild) {
        LogcatSharkLog.install()
    }
    
    // 会反射调用InternalLeakCanary
    LeakCanaryDelegate.loadLeakCanary(application)

    // 安装所有的内存监听器
    watchersToInstall.forEach {
        it.install()
    }
    // Only install after we're fully done with init.
    installCause = RuntimeException("manualInstall() first called here")
}

// 创建所有监听器
fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher //
): List<InstallableWatcher> {
    return listOf(
        ActivityWatcher(application, reachabilityWatcher),
        FragmentAndViewModelWatcher(application, reachabilityWatcher),
        RootViewWatcher(reachabilityWatcher),
        ServiceWatcher(reachabilityWatcher)
    )
}

// 全局单例,用于观察对象
val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
        check(isInstalled) {
            "AppWatcher not installed"
        }
        mainHandler.postDelayed(it, retainedDelayMillis)
    },
    isEnabled = { true }
)
ActivityWatcher类
kotlin 复制代码
class ActivityWatcher(
    private val application: Application,
    private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

    private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
        override fun onActivityDestroyed(activity: Activity) {
            // reachabilityWatcher也就是ObjectWather
            // 在Activity.onDestroy()时,观察该对象
            reachabilityWatcher.expectWeaklyReachable(
                activity, "${activity::class.java.name} received Activity#onDestroy() callback"
            )
        }
    }

    // 安装
    override fun install() {
        // 监听生命周期
        application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
    }

    override fun uninstall() {
        application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
    }
}
ObjectWatcher类基本属性
kotlin 复制代码
class ObjectWatcher {
    // 后台调度器
    private val checkRetainedExecutor: Executor
    // watchedObjects是Map集合,用于存放监控对象
    // 监控对象是KeyedWeakReference类型,是弱引用的包装类,内部持有监控对象和回收队列
    private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
    // 被GC回收的弱引用
    private val queue = ReferenceQueue<Any>()

   // 泄露对象数量
    val retainedObjectCount: Int
        @Synchronized get() {
            removeWeaklyReachableObjects()
            return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
        }
}
ObjectWatcher.expectWeaklyReachable()
kotlin 复制代码
@Synchronized override fun expectWeaklyReachable(
    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)
    
	// 放入观察集合
    watchedObjects[key] = reference
    
    // 延迟5s后执行
    checkRetainedExecutor.execute {
        moveToRetained(key)
    }
}
ObjectWatcher.removeWeaklyReachableObjects()
kotlin 复制代码
// 清理已回收的对象
private fun removeWeaklyReachableObjects() {
    var ref: KeyedWeakReference?
    do {
        ref = queue.poll() as KeyedWeakReference?
        if (ref != null) {
            // 从观察集合中移除已回收对象
            watchedObjects.remove(ref.key)
        }
    } while (ref != null)
}
ObjectWatcher.moveToRetained()
kotlin 复制代码
@Synchronized private fun moveToRetained(key: String) {
    // 先清理已回收对象
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
        retainedRef.retainedUptimeMillis = clock.uptimeMillis()
        // 回调通知LeakCanary处理,最终交给InternalLeakCanary.onObjectRetained()处理
        onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
}
InternalLeakCanary.onObjectRetained()
kotlin 复制代码
override fun onObjectRetained() = scheduleRetainedObjectCheck()

fun scheduleRetainedObjectCheck() {
    if (this::heapDumpTrigger.isInitialized) {
        heapDumpTrigger.scheduleRetainedObjectCheck()
    }
}
HeapDumpTrigger.scheduleRetainedObjectCheck()
kotlin 复制代码
// 检查泄露对象
fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
        return
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
        checkScheduledAt = 0
        checkRetainedObjects()
    }, delayMillis)
}
HeapDumpTrigger.checkRetainedObjects()
kotlin 复制代码
// 检查泄露对象
private fun checkRetainedObjects() {
    // 统计泄露对象数量
    var retainedReferenceCount = objectWatcher.retainedObjectCount

    if (retainedReferenceCount > 0) {
        // 主动触发GC,并等待100ms
        gcTrigger.runGc()
        // 重新获取泄漏对象计数
        retainedReferenceCount = objectWatcher.retainedObjectCount
    }

    // 检查泄露对象
    // 前台状态:阈值为5,小于阈值不dump
    // 后台状态:只要有泄露对象立即dump
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

    dismissRetainedCountNotification()
    val visibility = if (applicationVisible) "visible" else "not visible"
    
    // 触发dumpHeap
    dumpHeap(
        retainedReferenceCount = retainedReferenceCount,
        retry = true,
        reason = "$retainedReferenceCount retained objects, app is $visibility"
    )
}
HeapDumpTrigger.dumpHeap()
kotlin 复制代码
private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean,
    reason: String
) {
    // dump的存储文件
    val directoryProvider = InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
    val heapDumpFile = directoryProvider.newHeapDumpFile()

    val durationMillis: Long
    if (currentEventUniqueId == null) {
        currentEventUniqueId = UUID.randomUUID().toString()
    }
    try {
        InternalLeakCanary.sendEvent(DumpingHeap(currentEventUniqueId!!))
        if (heapDumpFile == null) {
            throw RuntimeException("Could not create heap dump file")
        }
        saveResourceIdNamesToMemory()
        val heapDumpUptimeMillis = SystemClock.uptimeMillis()
        KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
        durationMillis = measureDurationMillis {
            // 最终调用 Debug.dumpHprofData(heapDumpFile.absolutePath),堆转储过程(内存快照)
            configProvider().heapDumper.dumpHeap(heapDumpFile)
        }
        if (heapDumpFile.length() == 0L) {
            throw RuntimeException("Dumped heap file is 0 byte length")
        }
        lastDisplayedRetainedObjectCount = 0
        lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
        objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
        currentEventUniqueId = UUID.randomUUID().toString()
        InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))
    } catch (throwable: Throwable) {
    }
}
相关推荐
黄林晴2 小时前
紧急预警!Android 17 定位权限大改,你的 App 要适配了
android
夏沫琅琊2 小时前
Android API 发送短信技术文档
android·kotlin
周周不一样2 小时前
Android基础笔记1
android·笔记·gitee
取码网3 小时前
影视APP源码 SK影视 安卓+苹果双端APP 反编译详细视频教程+源码
android
musk12123 小时前
android webview 黑屏问题 , 页面加载时间有点长的情况下
android
夏沫琅琊3 小时前
Android 彩信导出技术文档
android·kotlin
sp42a3 小时前
安卓原生 MQTT 通讯 Java 实现
android·java·mqtt
Mr Lee_3 小时前
Apktool 反编译与回编译详解:enableOnBackInvokedCallback 属性缺失问题分析与解决
android
高梦轩8 小时前
MySQL高可用
android·运维·数据库