LeakCanary源碼分析

你們好,我是蒼王。
html

如下是我這個系列的相關文章,有興趣能夠參考一下,能夠給個喜歡或者關注個人文章。android

[Android]如何作一個崩潰率少於千分之三噶應用app--章節列表git

LeakCanary,金絲雀,在組件化Gank研發的時候,近來踩了一下坑,發現其內存泄露檢測的思想很是精妙。github


通讀在這篇文章你將會學會

1.知道LeakCanary對內存泄漏檢測的原理

2.如何安裝了應用後,隱藏和顯示應用在launcher的顯示

3.如何在主線程空閒時觸發任務

4.如何檢測頂層Activity的生命週期狀態

5.除了憑藉更改樣式顯示在TextView,是否還有其餘方法改變TextView的顯示樣式?

6.若是判斷進程是否在後臺


一.配置

能夠看到release debug所引用的leakcanary的工具是不一樣的。面試



工程目錄設計模式


其中leakcanary-android/leakcanary-android-no-op是依賴於leakcanary-analyzer。app


而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,也能夠掃碼進羣。我在這裏期待大家的加入!!!

相關文章
相關標籤/搜索