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) {
    }
}
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui