在使用LeakCanary的时候要引入:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'

debugImplementation  : debugImplementation 只在debug模式的编译和最终的debug apk打包时有效

LeakCanary的初始化是利用ContentProvider进行初始化的,

     <providerandroid:name="leakcanary.internal.AppWatcherInstaller$MainProcess"android:authorities="${applicationId}.leakcanary-installer"android:enabled="@bool/leak_canary_watcher_auto_install"android:exported="false" />

ContentProvider是在Application的onCreate的前面执行的,也就是在App启动的时候已经初始化好了。具体的ContentProvider是在什么时候执行的可以参考博客。

下面是AppWatcherInstaller的ContentProvider的onCreate代码:

 override fun onCreate(): Boolean {val application = context!!.applicationContext as ApplicationAppWatcher.manualInstall(application)return true}

AppWatcher是一个单例类只要作用:

The entry point API for using [ObjectWatcher] in an Android app. [AppWatcher.objectWatcher] is
* in charge of detecting retained objects, and [AppWatcher] is auto configured on app start to
* pass it activity and fragment instances. Call [ObjectWatcher.watch] on [objectWatcher] to
* watch any other object that you expect to be unreachable.

在Android应用中使用[ObjectWatcher]的入口点API。 *[AppWatcher.objectWatcher]负责检测保留的对象,[AppWatcher]在应用程序启动时自动配置为通过它的Activity和Fragment实例。

调用[objectWatcher]上的[ObjectWatcher.watch]以监视您期望无法访问的任何其他对象。

  /*** [AppWatcher] is automatically installed in the main process on startup. You can* disable this behavior by overriding the `leak_canary_watcher_auto_install` boolean resource:** ```* <?xml version="1.0" encoding="utf-8"?>* <resources>*   <bool name="leak_canary_watcher_auto_install">false</bool>* </resources>* ```** If you disabled automatic install then you can call this method to install [AppWatcher].*/fun manualInstall(application: Application) {InternalAppWatcher.install(application)}这是在values.xml中的一个字段
<?xml version="1.0" encoding="utf-8"?>
<resources><bool name="leak_canary_watcher_auto_install">true</bool>
</resources>

下面就是install的代码:

 fun install(application: Application) {checkMainThread()//检查是否在主线程,如果不是抛出异常if (this::application.isInitialized) {//判断这个lateinit的属性是否被复制return}SharkLog.logger = DefaultCanaryLog()//设置日志打印InternalAppWatcher.application = applicationval configProvider = { AppWatcher.config }//设置config//下面这两个就是观察Activity和Fragment是否泄漏的ActivityDestroyWatcher.install(application, objectWatcher, configProvider)FragmentDestroyWatcher.install(application, objectWatcher, configProvider)onAppWatcherInstalled(application)}
data class Config(/*** Whether AppWatcher should automatically watch destroyed activity instances.** Defaults to true.*/val watchActivities: Boolean = true,/*** Whether AppWatcher should automatically watch destroyed fragment instances.** Defaults to true.*/val watchFragments: Boolean = true,/*** Whether AppWatcher should automatically watch destroyed fragment view instances.** Defaults to true.*/val watchFragmentViews: Boolean = true,/*** Whether AppWatcher should automatically watch cleared [androidx.lifecycle.ViewModel]* instances.** Defaults to true.*/val watchViewModels: Boolean = true,/*** How long to wait before reporting a watched object as retained.** Default to 5 seconds.*/val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5),/*** Deprecated, this didn't need to be a part of the API.* Used to indicate whether AppWatcher should watch objects (by keeping weak references to* them). Currently a no-op.** If you do need to stop watching objects, simply don't pass them to [objectWatcher].*/@Deprecated("This didn't need to be a part of LeakCanary's API. No-Op.")val enabled: Boolean = true)

下面我们先分析这个Activity的写泄漏:主要是给application的registerActivityLifecycleCallbacks添加一个监听,

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)}}
}

下面就是在回调了Activity的onDestroy的方法后执行的内容:这个地方就是创建一个弱引用对象,进行观察,当执行完GC之后是否还存在,判断是否泄漏

/*** Watches the provided [watchedObject].** @param description Describes why the object is watched.*/@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] = referencecheckRetainedExecutor.execute {moveToRetained(key)}}
@Synchronized private fun moveToRetained(key: String) {removeWeaklyReachableObjects()val retainedRef = watchedObjects[key]if (retainedRef != null) {retainedRef.retainedUptimeMillis = clock.uptimeMillis()onObjectRetainedListeners.forEach { it.onObjectRetained() }}}

这个地方的onObjectRetained()方法是InternalLeakCanary中的onObjectRetained()方法可以看到调用的时候HeapDumpTrigger对象的onObjectRetained()方法下面看下这个方法

fun onObjectRetained() {scheduleRetainedObjectCheck(reason = "found new object retained",rescheduling = false)}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}//弱引用对象中保存的没有回收的Activity的数量var retainedReferenceCount = objectWatcher.retainedObjectCountif (retainedReferenceCount > 0) {//数量大于0执行GC,执行GC后再获取一次个数gcTrigger.runGc()retainedReferenceCount = objectWatcher.retainedObjectCount}//这个地方是检查回收对象的数量是否超过定义的阀值(5)如果没有就直接返回if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return//判断是否连接了调试解调器,如果是就直接返回if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {onRetainInstanceListener.onEvent(DebuggerIsAttached)showRetainedCountNotification(objectCount = retainedReferenceCount,contentText = application.getString(R.string.leak_canary_notification_retained_debugger_attached))scheduleRetainedObjectCheck(reason = "debugger is attached",rescheduling = true,delayMillis = WAIT_FOR_DEBUG_MILLIS)return}val now = SystemClock.uptimeMillis()val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis//如果时间小于预定义的5秒就Notification并且返回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)}
private fun dumpHeap(retainedReferenceCount: Int,retry: Boolean) {saveResourceIdNamesToMemory()val heapDumpUptimeMillis = SystemClock.uptimeMillis()KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillisval 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 = 0lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)//保存到文件后分析这个hprof文件HeapAnalyzerService.runAnalysis(application, heapDumpFile)}
fun runAnalysis(context: Context,heapDumpFile: File) {//启动一个前台服务分析堆文件val intent = Intent(context, HeapAnalyzerService::class.java)intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)startForegroundService(context, intent)}

下面就是hprof文件的分析了,LeakCanary用的Shark进行分析的。shark官网

LeakCanary的官网。

LeakCanary自动检测以下对象的泄漏:

  • 销毁Activity实例
  • 销毁Fragment实例
  • destroy fragment View实例
  • 清除ViewModel实例

它就会通过4个步骤自动检测并报告内存泄漏:

  1. 检测保留的对象。
  2. 转储堆。
  3. 分析堆。
  4. 分类泄漏。

详细的流程可以去官网看下,地址为https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/

LeakCanary简单分析相关推荐

  1. LeakCanary 原理分析

    LeakCanary 以1.5版本为例子,简单分析其中的原理. LeakCanary 可以检测App的内存泄漏,在我们自定义的 Application 的 onCreate() 方法中执行  Leak ...

  2. R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集、非线性:基函数展开和样条分析、你简单分析的不重要特征,可能只是线性不显著、而非线性是显著的

    R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集.非线性:基函数展开和样条分析.你简单分析的不重要特征,可能只是线性不显著.而非线性是显著的 目录

  3. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  4. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  5. howdoi 简单分析

    对howdoi的一个简单分析. 曾经看到过下面的这样一段js代码: try{doSth(); } catch (e){ask_url = "https://stackoverflow.com ...

  6. Mac与Phy组成原理的简单分析

    Mac与Phy组成原理的简单分析 2011-12-28 15:30:43 //http://blog.chinaunix.net/uid-20528014-id-3050217.html 本文乃fir ...

  7. python做数据可视化的代码_Python数据可视化正态分布简单分析及实现代码

    Python说来简单也简单,但是也不简单,尤其是再跟高数结合起来的时候... 正态分布(Normaldistribution),也称"常态分布",又名高斯分布(Gaussiandi ...

  8. ASIHTTPRequest源码简单分析

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比较底层的http库,功能比较少, ...

  9. Hessian 源码简单分析

    Hessian 源码简单分析 Hessian 是一个rpc框架, 我们需要先写一个服务端, 然后在客户端远程的调用它即可. 服务端: 服务端通常和spring 做集成. 首先写一个接口: public ...

最新文章

  1. Python 之 Pandas (七)merge合并
  2. 协程,又称微线程和纤程
  3. mysql列宽设置,mysql – 从.csv文件确定最佳列宽
  4. java多线程模拟龟兔赛跑
  5. Codeforces Round #425 D
  6. Microsoft Dynamics CRM 前瑞开发
  7. 关于局部变量在内存中的地址
  8. html文字置顶标签,HTML的marquee标签怎么用?
  9. java jps都卡死,java长时间运行后,jps失效
  10. Yoga安装Ubuntu后,wifi和亮度调节问题
  11. access建立两个字段唯一索引_面试官:谈谈你对mysql索引的认识?
  12. postgresql安装hypopg
  13. java统计中英文字数 Java问题通用解决代码
  14. Grad-CAM绘画热力图 使用教程 pycharm+anaconda 论文画图 卷积神经网络CNN Resnet
  15. 关于bootstrap自适应屏幕宽度学习
  16. 学会计学java,Java 属于以下哪种语言?_学小易找答案
  17. PAR(光合有效辐射)数据下载链接
  18. P1014 [NOIP1999 普及组] Cantor 表
  19. Activity启动过程源码分析
  20. annotation 的方法

热门文章

  1. 澳洲社区宜居指数:首都第一西澳垫底
  2. 调试工具(三):trace32
  3. 12306余票查询(爬虫)
  4. 一代电视购物巨头落幕,为何橡果国际以私有化退市告别美股?
  5. 想忘记都困难的Linux命令快速记忆法!
  6. codeblocks 调用dll/lib
  7. 如何去选择腾讯云服务器地域重庆和成都的分析
  8. 单臂路由原理与实验详情
  9. 2018/3/6 工作日志
  10. [已发表,转载勘误]Android upx脱壳