原理通過注冊activity和fragment的callback監(jiān)聽來觀察當前頁面是否destroy
Application中
監(jiān)聽activity 調用 registerActivityLifecycleCallbacks(this)
監(jiān)聽Fragment如下 :
// 注冊 Activity 生命周期監(jiān)聽器,以監(jiān)聽每個 Activity 的 Fragment 生命周期
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activity is FragmentActivity) {
// 對于 FragmentActivity,注冊 Fragment 生命周期回調
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) {
Log.d("FragmentLifecycle", "Fragment ${f.javaClass.simpleName} onCreated in ${activity.javaClass.simpleName}")
}
override fun onFragmentStarted(fm: FragmentManager, f: Fragment) {
Log.d("FragmentLifecycle", "Fragment ${f.javaClass.simpleName} onStarted in ${activity.javaClass.simpleName}")
}
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
Log.d("FragmentLifecycle", "Fragment ${f.javaClass.simpleName} onResumed in ${activity.javaClass.simpleName}")
}
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
Log.d("FragmentLifecycle", "Fragment ${f.javaClass.simpleName} onPaused in ${activity.javaClass.simpleName}")
}
override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
Log.d("FragmentLifecycle", "Fragment ${f.javaClass.simpleName} onStopped in ${activity.javaClass.simpleName}")
}
override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
Log.d("FragmentLifecycle", "Fragment ${f.javaClass.simpleName} onDestroyed in ${activity.javaClass.simpleName}")
}
}, true)
}
}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
})
當任意一個 Fragment 被創(chuàng)建、啟動、恢復、暫停、停止或銷毀時,都會通過 FragmentLifecycleCallbacks 打印日志。
通過這種方式,你可以在全局范圍內監(jiān)聽每個 Activity 中的 Fragment 的生命周期。
在onDestroy方法中檢測到對象的弱引用還有沒有值,有值說明沒有被回收存在內存泄漏,沒有值說明已經被回收了,當前對象不存在泄漏
存在問題:
-
性能開銷(只能檢測debug環(huán)境 線上會很卡頓(線上檢測方式可以用KOOM類庫快手開源的))
image.png
內存和CPU負擔:LeakCanary 在進行內存快照(Heap Dump)和分析時會消耗額外的內存和CPU資源。對于較大的應用或復雜的場景,生成和分析堆內存快照可能會導致明顯的性能下降,尤其是在低性能設備上。
內存使用增加:LeakCanary 本身會消耗一定的內存來跟蹤對象,并且 Heap Dump 文件會占用設備存儲空間,這可能導致在調試環(huán)境中內存壓力增大。
誤報問題
假陽性(False Positive):由于 LeakCanary 基于弱引用和GC回收機制來判斷對象是否被泄漏,在某些情況下,系統(tǒng)可能延遲回收對象或者強制回收未及時發(fā)生,導致 LeakCanary 誤報內存泄漏。
復雜場景的誤報:對于某些復雜的引用關系(如系統(tǒng)級別的引用或長期存在的靜態(tài)引用),LeakCanary 可能無法準確判斷是否為實際內存泄漏,尤其是在多線程或異步操作較多的情況下。嵌套的fragment會被漏掉檢查
比如app首頁有三個tab(fragment) 在tab1中 嵌套了一個viewpager,viewpager中又有很多fragment,LeakCanary源碼中原理是通過activity的fragmentManager中監(jiān)聽第一層的fragment,無法監(jiān)聽到第二層第三層的所以會導致漏掉檢查。
4 activity也會存在此問題
LeakCanary觀察的是能夠執(zhí)行到onDestroy生命周期的類,如果觀察不到那就會漏掉,申請情況下會觀察不到呢 以下舉例說明:
activity A啟動activity B
在 Android 中,當 ActivityA 啟動 ActivityB 時,ActivityA 的生命周期會隨著 ActivityB 的啟動過程發(fā)生變化。具體來說:
ActivityB 的啟動:當 ActivityB 被啟動時,它的生命周期依次執(zhí)行 onCreate()、onStart() 和 onResume()。
ActivityA 的 onStop():當 ActivityB 完成 onResume(),也就是 ActivityB 顯示在前臺并獲得焦點時,此時 ActivityA 會進入后臺,然后 ActivityA 的 onStop() 方法被調用。
源碼分析:為什么ActivityB 完成 onResume()后才執(zhí)行ActivityA 的 onStop()
因為AMS的管理機制,當 ActivityB 執(zhí)行onResume()的時候 消息隊列中會add一個idlehandler 告訴Activity主線程空閑了,然后會通知ams ams收到這個消息后會通知ActivityA該執(zhí)行onStop了.
如果在執(zhí)行ActivityB onResume()的時候一直在刷新UI 比如動畫此時Activity主線程沒有空閑,所以就不會執(zhí)行idlehandler ams就不會通知ActivityA 執(zhí)行 onStop() 所以就會導致ActivityA無法被回收。 當 ActivityB 執(zhí)行onResume()的時候 AMS會設置一個定時機制10s后會通知ActivityA 執(zhí)行onStop方法
(補充和詳細分析:
- AMS 與消息隊列的調度機制
當 ActivityB 執(zhí)行到 onResume() 時,它會獲取焦點并進入前臺。此時,AMS 會將一個 IdleHandler 添加到 ActivityThread 的消息隊列中,這個 IdleHandler 負責通知 AMS ActivityB 進入前臺且 ActivityA 已經不再處于活躍狀態(tài),因此可以讓 ActivityA 執(zhí)行 onStop()。
這里的關鍵點是:消息隊列的空閑狀態(tài)。如果 ActivityB 在 onResume() 階段執(zhí)行耗時任務(例如動畫、UI 刷新等),消息隊列可能不會很快進入空閑狀態(tài),導致 IdleHandler 沒有機會執(zhí)行,從而延遲 ActivityA 的 onStop()。
AMS 定時機制
AMS 確實會設置一個超時機制(通常是 10 秒左右),以確保 ActivityA 能及時執(zhí)行 onStop()。這個定時機制是為了防止某些情況下 ActivityThread 長時間無法進入空閑狀態(tài),導致 onStop() 被過度延遲。如果超時后還沒有進入空閑狀態(tài),AMS 會強制要求 ActivityA 執(zhí)行 onStop() 以避免資源浪費。進一步分析和建議
UI 線程占用問題:如果 ActivityB 在 onResume() 期間執(zhí)行了大量的 UI 刷新操作或者動畫,使得主線程忙碌而無法空閑,確實會延遲 IdleHandler 的執(zhí)行。為了解決這種情況,可以考慮將耗時操作(如動畫或復雜 UI 刷新)放在合適的地方進行,比如在 onPostResume()、onWindowFocusChanged() 或者通過 Handler 延遲執(zhí)行,這樣可以確保主線程有足夠的時間空閑,從而讓 IdleHandler 觸發(fā) onStop()。
定時機制的作用:AMS 的定時機制是作為安全網的一部分,用來保證生命周期的正確性。然而,依賴定時機制并不是最佳實踐,因為這可能會導致性能問題或內存泄漏。應盡量避免阻塞 onResume() 的主線程操作。
總的來說,你的理解是正確的,但需要注意的是,延遲 onStop() 并不是常態(tài),應該盡量優(yōu)化 Activity 的啟動和 UI 刷新操作,避免占用主線程資源過多導致生命周期回調被阻塞。)