LeakCanary2 之 LeakSentry源码分析

875 阅读4分钟

前言

LeakCanary是Square公司为Android开发者提供的一个自动检测内存泄漏的工具,在4月23日推出了2.0预览版,更新内容见Github,其中新增了一个LeakSentry库,该库作为LeakCanary的开关,可以实时查看那些被观察的对象是否可能内存泄露,并且可以独立引入使用,下面从源码角度来分析下该库。

源码分析

首先查看其manifest文件,发现其中注册了一个名叫LeakSentryInstaller的ContentProvider,因此其onCreate方法会在Application#onCreate之前得到执行,原因如下

// ActivityThread.java
private void handleBindApplication() {
    ...
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
    }
    mInstrumentation.callApplicationOnCreate(app);
}
private void installContentProviders(...) {
    installProvider(...);
}
private IActivityManager.ContentProviderHolder installProvider(...) {
    ContentProvider localProvider = null;
    localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();
    localProvider.attachInfo(c, info);
}
// ContentProvider.java
private void attachInfo(...) {
    if (mContext == null) {
        ...
        ContentProvider.this.onCreate();
    }
}

搞清楚了入口以后,看看LeakSentryInstaller#onCreate都干了些什么

// LeakSentryInstaller.kt
override fun onCreate(): Boolean {
    CanaryLog.logger = DefaultCanaryLog()
    val application = context!!.applicationContext as Application
    InternalLeakSentry.install(application)
    return true
}
internal object InternalLeakSentry {
    init {
        listener = try {
            val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
            leakCanaryListener.getDeclaredField("INSTANCE").get(null) as LeakSentryListener
        } catch (ignored: Throwable) {
            LeakSentryListener.None
        }
    }
}
fun install(application: Application) {
    ...
    InternalLeakSentry.application = application
    val configProvider = { LeakSentry.config }
    ActivityDestroyWatcher.install(application, refWatcher, configProvider)
    FragmentDestroyWatcher.install(application, refWatcher, configProvider)
    listener.onLeakSentryInstalled(application)
}
object LeakSentry {
    data class Config(
            val enabled: Boolean = InternalLeakSentry.isDebuggableBuild,
            val watchActivities: Boolean = true,
            val watchFragments: Boolean = true,
            val watchFragmentViews: Boolean = true,
            val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5))
    @Volatile
    var config: Config = if (isInstalled) Config() else Config(enabled = false)
    val refWatcher
        get() = InternalLeakSentry.refWatcher
    val isInstalled
        get() = InternalLeakSentry.isInstalled
    fun manualInstall(application: Application) = InternalLeakSentry.install(application)
}

onCreate内部调用了InternalLeakSentry#install,在其init代码块设置了监听器当LeakCanary被依赖时会设置成InternalLeakCanary实例,不然就是一个空实现,install内部主要就是调用了两个Watch#install

ActivityDestroyWatcher#install

// ActivityDestroyWatcher.kt
internal class ActivityDestroyWatcher private constructor(
        private val refWatcher: RefWatcher,
        private val configProvider: () -> Config) {
    private val lifecycleCallbacks = object : ActivityLifecycleCallbacksAdapter() {
        override fun onActivityDestroyed(activity: Activity) {
            if (configProvider().watchActivities) {
                refWatcher.watch(activity)
            }
        }
    }
    companion object {
        fun install(
                application: Application,
                refWatcher: RefWatcher,
                configProvider: () -> Config) {
            val activityDestroyWatcher =
                    ActivityDestroyWatcher(refWatcher, configProvider)
            application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
        }
    }
}

内部注册了一个Activity生命周期回调,当onDestory事件来临时由于默认的config.watchActivities = true,因此refWatcher#water将被调用

class RefWatcher constructor(
        private val clock: Clock,
        private val checkRetainedExecutor: Executor,
        private val onReferenceRetained: () -> Unit,
        private val isEnabled: () -> Boolean = { true }) {

    private val watchedReferences = mutableMapOf<String, KeyedWeakReference>()
    private val retainedReferences = mutableMapOf<String, KeyedWeakReference>()
    private val queue = ReferenceQueue<Any>()

    val hasRetainedReferences: Boolean
        @Synchronized get() {
            removeWeaklyReachableReferences()
            return retainedReferences.isNotEmpty()
        }
    val hasWatchedReferences: Boolean
        @Synchronized get() {
            removeWeaklyReachableReferences()
            return retainedReferences.isNotEmpty() || watchedReferences.isNotEmpty()
        }
    val retainedKeys: Set<String>
        @Synchronized get() {
            removeWeaklyReachableReferences()
            return HashSet(retainedReferences.keys)
        }
    @Synchronized
    fun watch(watchedReference: Any) {
        watch(watchedReference, "")
    }
    @Synchronized
    fun watch(
            watchedReference: Any,
            referenceName: String) {
        if (!isEnabled()) {
            return
        }
        removeWeaklyReachableReferences()
        val key = UUID.randomUUID()
                .toString()
        val watchUptimeMillis = clock.uptimeMillis()
        val reference =
                KeyedWeakReference(watchedReference, key, referenceName, watchUptimeMillis, queue)
        watchedReferences[key] = reference
        checkRetainedExecutor.execute {
            moveToRetained(key)
        }
    }
    @Synchronized
    private fun moveToRetained(key: String) {
        removeWeaklyReachableReferences()
        val retainedRef = watchedReferences.remove(key)
        if (retainedRef != null) {
            retainedReferences[key] = retainedRef
            onReferenceRetained()
        }
    }
    @Synchronized
    fun removeRetainedKeys(keysToRemove: Set<String>) {
        retainedReferences.keys.removeAll(keysToRemove)
    }
    @Synchronized
    fun clearWatchedReferences() {
        watchedReferences.clear()
        retainedReferences.clear()
    }
    private fun removeWeaklyReachableReferences() {
        var ref: KeyedWeakReference?
        do {
            ref = queue.poll() as KeyedWeakReference?
            if (ref != null) {
                val removedRef = watchedReferences.remove(ref.key)
                if (removedRef == null) {
                    retainedReferences.remove(ref.key)
                }
            }
        } while (ref != null)
    }
}

watch方法主要分为如下几步

  1. 当isEnabled被设置成false,那么watch将会直接返回,该值取自于config
  2. 移除掉那些已经在队列里面的弱引用,一开始肯定没有先跳过
  3. 创建一个KeyedWeakReference实例(派生自WeakReference),传入queue,当被观察的对象被回收掉以后会将弱引用对象放入该队列中去
  4. 将弱引用对象放入watchedReferences中
  5. 延迟一定时间后(默认为5秒,取自于config)调用moveToRetained

moveToRetained首先调用了removeWeaklyReachableReferences(),该方法将那些已经放到队列中的弱引用对象从watchedReferences中移除(存在队列中就表示该对象已经被回收了不可能发生内存泄露),后面的retainedReferences#remove是为了将上次保留的对象删除(因为本次它已经被回收了)。接着如果retainedRef!=null,那么就说明该对象还没有被回收,就加入到retainedReferences中,并回调listener#onReferenceRetained

FragmentDestroyWatcher#install

internal interface FragmentDestroyWatcher {
    fun watchFragments(activity: Activity)
    companion object {
        private const val SUPPORT_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"
        fun install(
                application: Application,
                refWatcher: RefWatcher,
                configProvider: () -> LeakSentry.Config) {
            val fragmentDestroyWatchers = mutableListOf<FragmentDestroyWatcher>()
            if (SDK_INT >= O) {
                fragmentDestroyWatchers.add(AndroidOFragmentDestroyWatcher(refWatcher, configProvider))
            }
            if (classAvailable(SUPPORT_FRAGMENT_CLASS_NAME)) {
                fragmentDestroyWatchers.add(SupportFragmentDestroyWatcher(refWatcher, configProvider))
            }
            if (fragmentDestroyWatchers.size == 0) {
                return
            }
            application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacksAdapter() {
                override fun onActivityCreated(
                        activity: Activity,
                        savedInstanceState: Bundle?) {
                    for (watcher in fragmentDestroyWatchers) {
                        watcher.watchFragments(activity)
                    }
                }
            })
        }
        private fun classAvailable(className: String): Boolean {
            return try {
                Class.forName(className)
                true
            } catch (e: ClassNotFoundException) {
                false
            }
        }
    }
}

首先FragmentDestroyWatcher只支持Android8及以上或者是AndroidX,还是监听了Activity声明周期不过这次监听onCreate,先看看AndroidOFragmentDestroyWatcher#watchFragments

AndroidOFragmentDestroyWatcher#watchFragments

internal class AndroidOFragmentDestroyWatcher(
        private val refWatcher: RefWatcher,
        private val configProvider: () -> Config
) : FragmentDestroyWatcher {
    private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
        override fun onFragmentViewDestroyed(
                fm: FragmentManager,
                fragment: Fragment) {
            val view = fragment.view
            if (view != null && configProvider().watchFragmentViews) {
                refWatcher.watch(view)
            }
        }
        override fun onFragmentDestroyed(
                fm: FragmentManager,
                fragment: Fragment) {
            if (configProvider().watchFragments) {
                refWatcher.watch(fragment)
            }
        }
    }
    override fun watchFragments(activity: Activity) {
        val fragmentManager = activity.fragmentManager
        fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
    }
}

内部注册了Fragment声明周期回调,由于该方法只有在API26及以上才有,因此前面判断了Android O,接着当Fragment被销毁后会分别调用refWatcher.watch(view)、refWatcher.watch(fragment),这里面的逻辑上文已经说过了

AndroidOFragmentDestroyWatcher#watchFragments

internal class SupportFragmentDestroyWatcher(
        private val refWatcher: RefWatcher,
        private val configProvider: () -> Config) : FragmentDestroyWatcher {
    private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
        override fun onFragmentViewDestroyed(
                fm: FragmentManager,
                fragment: Fragment) {
            val view = fragment.view
            if (view != null && configProvider().watchFragmentViews) {
                refWatcher.watch(view)
            }
        }
        override fun onFragmentDestroyed(
                fm: FragmentManager,
                fragment: Fragment) {
            if (configProvider().watchFragments) {
                refWatcher.watch(fragment)
            }
        }
    }
    override fun watchFragments(activity: Activity) {
        if (activity is FragmentActivity) {
            val supportFragmentManager = activity.supportFragmentManager
            supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
        }
    }
}

内部逻辑与AndroidOFragmentDestroyWatcher一样,只是获取的是supportFragmentManager

总结

根据源码可以看出LeakSentry内部使用弱引用保存被观察对象,在指定时间后如果还未被回收就可以认为该对象可能发生内存泄露,其默认会自动观察销毁的Activity、fragment及其内部View,同时也可以手动使用LeakSentry.refWatcher.watch(obj)来观察任意不再需要的对象,然后在代码中通过调用LeakSentry.refWatcher.retainedKeys.size查看还有几个被观察对象还未被释放