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銷燬時機的,下篇文章再見!!