LeakCanary如何監聽Fragment、Fragment View、ViewModel銷燬時機?

語言: CN / TW / HK

theme: juejin highlight: a11y-light


持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第5天,點擊查看活動詳情

本篇文章主要是分析下LeakCanary如何監聽Fragment、Fragment View、ViewModel銷燬時機的,至於老話常談的Activity銷燬監聽大家都知道,就不在此過於闡述了。

添加依賴,基於最新版本分析

groovy dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' }

前置知識

  1. LeakCanary是藉助ContentProvider來實現自動初始化的:

image.png

我們可以通過顯示在xml中設置leak_canary_watcher_auto_installfalse來關閉其自動初始化。

  1. ContentProvideronCreate()方法會調用AppWatcher.manualInstall(),看下這個方法:

image.png

我們要關注的就是watchersToInstall這個參數,這個參數默認使用appDefaultWatchers()方法進行賦值,走進去看下:

image.png

這個裏面就返回了監聽Activity、Service、Root View、Fragment、ViewModel要用到的監聽對象,看到類名就能知曉它是用來監聽什麼對象銷燬的,接下來我們來一個個的進行分析(除了ActivityWatcher)。

監聽Fragment銷燬時機

這個使用到的就是FragmentAndViewModelWatcher這個對象實現的,看下里面源碼:

image.png

  1. 如果當前SDK版本是8.0及以上就添加AndroidOFragmentDestroyWatcher監聽Fragment銷燬時機;
  2. 如果當前是Androidx版本的Fragment,就添加AndroidXFragmentDestroyWatcher監聽銷燬;
  3. 如果是supoort下的Fragment,就添加AndroidSupportFragmentDestroyWatcher監聽銷燬;

PS:這個地方使用了一個小技巧,如何分別兼容androidxsupport下的Fragment監聽:

分別嘗試去反射androidx下的androidx.fragment.app.Fragmentsupport下的android.support.v4.app.Fragment,反射失敗了就代表不存在當前的包名路徑,就不會添加對應的監聽,反之就添加對應監聽。

這個地方我們就看下AndroidXFragmentDestroyWatcher如何監聽銷燬Fragment的:

image.png 創建了一個FragmentManager.FragmentLifecycleCallbacks的子類,這個裏面就有我們非常熟悉的監聽Fragment生命週期的方法。

我們要監聽的是Fragment的銷燬時機,那就直接重寫onFragmentDestroyed方法,將該Fragment對象添加泄漏觀察:

image.png

除此之外,我們還發現重寫了onFragmentViewDestroyed方法,這就説明還可以監聽FragmentView是否發生了內存泄漏。畢竟,FragmentView生命週期和Fragment生命週期不一定是同步的。

image.png

最終我們將這個自定義的FragmentManager.FragmentLifecycleCallbacks的子類對象添加到ActivityFragmentManager中從而實現監聽:

image.png

監聽ViewModel銷燬時機

實現ViewModel監聽的類也是FragmentAndViewModelWatcherActivityFragment中的ViewModel銷燬監聽都能監聽得到: 1. 監聽FragmentViewModel銷燬的監聽類的注入時機就是發生在上面一節自定義FragmentManager.FragmentLifecycleCallbacksonFragmentCreated方法中:

image.png

  1. 監聽ActivityViewModel銷燬的監聽類的注入時機是在調用registerFragmentLifecycleCallbacks注入Fragment生命週期監聽的同一處:

image.png

可以發現最終都會調用ViewModelClearedWatcher類實現監聽,只不過ActivityFragment各自傳入的ViewModelStore不同。

PS:ViewModelStore裏面有一個HashMap<String, ViewModel>類型的map對象保存ViewModel

看下ViewModelClearedWatcher這個類:

image.png

它繼承了ViewModel這個類,並且將該類注入到了ActivityFragment對應的ViewModelStore中:

image.png

PS: 由於需要創建帶構造參數的ViewModel,所以需要自定義一個Factory實現構造參數的注入

然後重寫了onClear()方法:

image.png

在該方法中,反射獲取ViewModelStore中的mMap集合中保存的所有ViewModel,而onClear()方法只有在ActivityFragment非因配置的更改發生的銷燬中被執行,所以當onClear()執行時,就代表ActivityFragment要銷燬了,自然能裏面的所有ViewModel應該要被銷燬了,依次添加到內存泄漏的監聽中。

到了這裏大家應該明白了,LeakCannary通過向ActivityFragmentViewModelStore注入一個自定義的ViewModel並監聽其onClear()的執行時機,從而實現監聽ActivityFragment中所有ViewModel的銷燬時機,並添加到內存泄漏監聽中。

最後

還會有一篇文章來分析LeakCanary如何實現Root ViewService銷燬時機的,下篇文章再見!!