android leakcanary 源码分析,LeakCanary源码分析

大家好,我是苍王。

以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

LeakCanary,金丝雀,在组件化Gank研发的时候,近来踩了一下坑,发现其内存泄露检测的思想非常精妙。

通读在这编文章你将会学会

1.知道LeakCanary对内存泄漏检测的原理

2.如何安装了应用后,隐藏和显示应用在launcher的显示

3.如何在主线程空闲时触发任务

4.如何检测顶层Activity的生命周期状态

5.除了凭借更改样式显示在TextView,是否还有其他方法改变TextView的显示样式?

6.如果判断进程是否在后台

一.配置

可以看到release debug所引用的leakcanary的工具是不同的。

481775d198f0

工程目录

481775d198f0

其中leakcanary-android/leakcanary-android-no-op是依赖于leakcanary-analyzer。

481775d198f0

而leakcanary-analyzer依赖于leakcanary-watcher

481775d198f0

工程使用的时候,需要在自定义的Application中使用

481775d198f0

其还使用使用了StrictMode,这里有StrictMode详细介绍

481775d198f0

二.源码分析

RefWatcher对象的安装

481775d198f0

构建一个AndroidRefWatcherBuilder的对象

481775d198f0

其是继承于RefWatcherBuilder对象的

481775d198f0

设置后台的监听

481775d198f0

481775d198f0

其设置的监听者是用于分析泄露结果,和发送通知

481775d198f0

481775d198f0

在RefWatcherBuilder中设置excluedeRefs

481775d198f0

481775d198f0

这里创建泄露路径的记录建造者

481775d198f0

其最终会build方法创建一个ExcludedRefs的对象

其参数用于记录泄露对象的路径参数

481775d198f0

使用buildAndAInstall构建出RefWatcher对象

481775d198f0

可以看出只支持4.0 ICS以上的

481775d198f0

这里在RefWatcherBuilder利用建造者设计模式设置参数

481775d198f0

关于具体这些参数分析

1.watchExecutor 是线程控制器,控制activity销毁后5s再去观察泄露情况

2.debuggerControl 控制debugger的,并未编写任何执行事件

3.gcTrigger 用来触发垃圾回收的,上面的线程控制器5s后观察有泄露,不算泄露,必须垃圾回收后,再去观察一次。所以最多会观察两次。第一次是5s后观察,第二次是5s后在垃圾回收后观察。

4.heapDumpListener hprof文件解释完后,会告诉这个监听者。这个监听者就会更新状态栏

5.excludedRefs 这玩意是做额外处理的,这里面定义了一些类,如果是这些类泄露了,不会提示的。例如我就是想我的Activity泄露,你别管我,那你可以把类名加到excludedRefs 中。

481775d198f0

这里面还要打开显示泄露的Activity

481775d198f0

这里面DisplayLeakActivity就是显示泄露的

481775d198f0

我们看到AndroidManifest里面组件默认enabled =false的

而且这里可以看出HeapAnalyzerService分析内存泄露的后台是新的进程

481775d198f0

这里使用PackageManager的setComponedtEnabledSettings开启。

481775d198f0

正是这样,才能一开始未存在泄露时隐藏leakCanary的图标。

通过ActivityRefWatcher.install启动监听泄露

481775d198f0

481775d198f0

通过监听application.registerActivityLifecyleCallbacks的方法来监听周期

481775d198f0

三.泄露过程分析

通过查看监听ActivityLifecycleCallbacks的回调监听destroy的方法。

481775d198f0

启动对前台的Activity的检测

481775d198f0

转化为对象去观看

481775d198f0

可以看到此时创建一个持有Activity的弱引用

481775d198f0

通过watchExcuter执行调用

481775d198f0

这里面会是用的是AndroidWatchExcuter的对象

481775d198f0

其时间是5秒

481775d198f0

我们这里可以看到其会去持有主消息队列和创建出线程的消息队列

481775d198f0

然后调用execute的方法来,无论waitForIdle或是postWaitForIdle,都是需要切换到主队列。

然后使用MessageQueue.IdleHandler可以用来在线程空闲的时候,指定一个操作,使用IdleHandler的好处在于可以不用指定一个将来时间,只要线程空闲了,就可以执行它指定的操作。

481775d198f0

这里是使用Retryable,是有枚举状态的,如果是RETRY的返回状态,会重新执行。

481775d198f0

AndroidWatchExcuter会调用ensureGone的方法

481775d198f0

这里会需要试着移除弱引用,

481775d198f0

判断队列里面是是否有对应Activity的弱引用对象

然后试着在retainedKeys集合里面试着移除

481775d198f0

再次判断是否在集合中是否还包含引用对象,如果不包含就说明没有泄露

481775d198f0

这里继续会走GC内存回收的流程

481775d198f0

RunTime.getRunTime().gc()触发系统gc操作

enqueueReference通过强制限制100毫秒的时间给gc

System.runFinalization()是强制调用已经失去引用的对象的finalize方法

481775d198f0

然后再进行弱引用对象移除

在判断如果依然没被移除,会调用heapDumper.dumpHeap的方法

481775d198f0

这里泄露目录的定义

481775d198f0

默认最多七个Dump文件

481775d198f0

这里调用dumpHeap会创建文件.hrof的文件

newHeapDumpFile来创建.hrof文件

这里使用Debug.dumpHprofData这个类,是heap堆的快照,可以获知程序的哪些部分正在使用大部分的内存

481775d198f0

然后使用heapdumpListener.analyze的方法分析堆内存

481775d198f0

这里heapdumpListener使用的是AndroidRefWatcherBuilder里面的

481775d198f0

进一步看到是使用HeapAnalyzerService.runAnalysis的方法

481775d198f0

这里是启动一个其自身作为一个IntentService,

481775d198f0

启动IntentService就会直接运行onHanldeIntent的方法,运行完就会释放回收掉Service

这里HeapAnalyzer会使用checkForLeak的方法来分析内存泄露结果

然后回调结果给上层的DisplayLeakService来处理结果

481775d198f0

会调用findLeakTrace的侦测

481775d198f0

这里建立检测泄露节点和返回泄露对象的详细信息。

481775d198f0

这里最终会调用到squaredup的haha库,专用于分析Android的堆分析

481775d198f0

我们看看将结果回调给Service的时候,会启动Service,然后将相应的result参数传递过去

481775d198f0

我们看到IntentService,会调用onHeapAnalyzed的方法

481775d198f0

DisplayLeakService被启动后读取相应的参数

481775d198f0

可以看到设置了一些显示的参数后,会提示Notification和处理堆

481775d198f0

LeackCanaryInternals发送Notification到状态栏

481775d198f0

其通知的pendingIntent实际跳转到DisplayLeakActivity

481775d198f0

这里提供了最终调用的方法给用户自己处理。

481775d198f0

关于DisplayLeakActivity就是要将AnalysisResult展示出来。

其使用LIstView显示路径的时候,TextView的解析是使用html.forHtml来解析HTML文本拼接

481775d198f0

我们可以看到其使用elementToHtmlString来拼接Html文本

481775d198f0

然后回到一开始的Application装载LeakCanary,

其使用isInAnalyzerProcess分析应用函数

481775d198f0

判断是否在后台进程

481775d198f0

然后这里LeakCanary封装了一个非常好的判断是否后台进程的方法。

481775d198f0

总结

显然leakCanary的设计非常精妙,可以非常容易检测的大部分内存泄漏了。

但是暂时发现的缺陷

1.如果首页的Activity一直不销毁(onDestroy)那么将一直无法检测到首页的调用栈的内存泄漏

2.无法检测Service产生的内存泄漏。

这节就到这里,

下一节将会更精彩,敬请期待!!!

群号是316556016,也可以扫码进群。我在这里期待你们的加入!!!

481775d198f0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值