LeakCanary源码分析

作者: CangWang | 来源:发表于2017-04-10 10:35 被阅读1833次

    大家好,我是苍王。

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

    [Android]如何做一个崩溃率少于千分之三噶应用app--章节列表

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

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

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

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

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

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

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

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

    一.配置

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

    工程目录

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

    而leakcanary-analyzer依赖于leakcanary-watcher

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

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

    二.源码分析

    RefWatcher对象的安装

    构建一个AndroidRefWatcherBuilder的对象

    其是继承于RefWatcherBuilder对象的

    设置后台的监听

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

    在RefWatcherBuilder中设置excluedeRefs

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

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

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

    使用buildAndAInstall构建出RefWatcher对象

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

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

    关于具体这些参数分析

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

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

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

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

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

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

    这里面DisplayLeakActivity就是显示泄露的

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

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

    这里使用PackageManager的setComponedtEnabledSettings开启。

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

    通过ActivityRefWatcher.install启动监听泄露

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

    三.泄露过程分析

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

    启动对前台的Activity的检测

    转化为对象去观看

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

    通过watchExcuter执行调用

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

    其时间是5秒

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

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

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

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

    AndroidWatchExcuter会调用ensureGone的方法

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

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

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

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

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

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

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

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

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

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

    这里泄露目录的定义

    默认最多七个Dump文件

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

    newHeapDumpFile来创建.hrof文件

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

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

    这里heapdumpListener使用的是AndroidRefWatcherBuilder里面的

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

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

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

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

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

    会调用findLeakTrace的侦测

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

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

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

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

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

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

    LeackCanaryInternals发送Notification到状态栏

    其通知的pendingIntent实际跳转到DisplayLeakActivity

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

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

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

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

    具体Html文本的用法可以参考Android TextView使用HTML处理字体样式、显示图片等

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

    其使用isInAnalyzerProcess分析应用函数

    判断是否在后台进程

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

    总结

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

    但是暂时发现的缺陷

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

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

    这节就到这里,

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

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

    相关文章

      网友评论

      • xifanoo01:这个源码分析很好,有怎么使用的文章么?
        CangWang:@xifanoo01 使用的文章太多啦

      本文标题:LeakCanary源码分析

      本文链接:https://www.haomeiwen.com/subject/eatxattx.html