LeakCanary源碼分析

原理通過注冊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方法中檢測到對象的弱引用還有沒有值败许,有值說明沒有被回收存在內存泄漏,沒有值說明已經被回收了淑蔚,當前對象不存在泄漏

存在問題:

  1. 性能開銷(只能檢測debug環(huán)境 線上會很卡頓(線上檢測方式可以用KOOM類庫快手開源的))


    image.png

內存和CPU負擔:LeakCanary 在進行內存快照(Heap Dump)和分析時會消耗額外的內存和CPU資源市殷。對于較大的應用或復雜的場景,生成和分析堆內存快照可能會導致明顯的性能下降刹衫,尤其是在低性能設備上醋寝。
內存使用增加:LeakCanary 本身會消耗一定的內存來跟蹤對象,并且 Heap Dump 文件會占用設備存儲空間带迟,這可能導致在調試環(huán)境中內存壓力增大音羞。

  1. 誤報問題
    假陽性(False Positive):由于 LeakCanary 基于弱引用和GC回收機制來判斷對象是否被泄漏,在某些情況下邮旷,系統(tǒng)可能延遲回收對象或者強制回收未及時發(fā)生黄选,導致 LeakCanary 誤報內存泄漏。
    復雜場景的誤報:對于某些復雜的引用關系(如系統(tǒng)級別的引用或長期存在的靜態(tài)引用)婶肩,LeakCanary 可能無法準確判斷是否為實際內存泄漏办陷,尤其是在多線程或異步操作較多的情況下。

  2. 嵌套的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方法
(補充和詳細分析:

  1. 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()召衔。

  1. AMS 定時機制
    AMS 確實會設置一個超時機制(通常是 10 秒左右)严蓖,以確保 ActivityA 能及時執(zhí)行 onStop()。這個定時機制是為了防止某些情況下 ActivityThread 長時間無法進入空閑狀態(tài)吩坝,導致 onStop() 被過度延遲钉寝。如果超時后還沒有進入空閑狀態(tài),AMS 會強制要求 ActivityA 執(zhí)行 onStop() 以避免資源浪費逮走。

  2. 進一步分析和建議
    UI 線程占用問題:如果 ActivityB 在 onResume() 期間執(zhí)行了大量的 UI 刷新操作或者動畫,使得主線程忙碌而無法空閑,確實會延遲 IdleHandler 的執(zhí)行窿锉。為了解決這種情況嗡载,可以考慮將耗時操作(如動畫或復雜 UI 刷新)放在合適的地方進行鼻疮,比如在 onPostResume()、onWindowFocusChanged() 或者通過 Handler 延遲執(zhí)行琳轿,這樣可以確保主線程有足夠的時間空閑,從而讓 IdleHandler 觸發(fā) onStop()崭篡。

定時機制的作用:AMS 的定時機制是作為安全網的一部分,用來保證生命周期的正確性琉闪。然而,依賴定時機制并不是最佳實踐,因為這可能會導致性能問題或內存泄漏斯入。應盡量避免阻塞 onResume() 的主線程操作砂碉。

總的來說刻两,你的理解是正確的,但需要注意的是滋迈,延遲 onStop() 并不是常態(tài)帝美,應該盡量優(yōu)化 Activity 的啟動和 UI 刷新操作硕旗,避免占用主線程資源過多導致生命周期回調被阻塞。)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末女责,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子创译,更是在濱河造成了極大的恐慌抵知,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件软族,死亡現場離奇詭異刷喜,居然都是意外死亡,警方通過查閱死者的電腦和手機立砸,發(fā)現死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門掖疮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颗祝,你說我怎么就攤上這事浊闪。” “怎么了螺戳?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵搁宾,是天一觀的道長。 經常有香客問我倔幼,道長盖腿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮翩腐,結果婚禮上鸟款,老公的妹妹穿的比我還像新娘。我一直安慰自己茂卦,他們只是感情好何什,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疙筹,像睡著了一般富俄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上而咆,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天霍比,我揣著相機與錄音,去河邊找鬼暴备。 笑死悠瞬,一個胖子當著我的面吹牛,可吹牛的內容都是我干的涯捻。 我是一名探鬼主播浅妆,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼障癌!你這毒婦竟也來了凌外?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤涛浙,失蹤者是張志新(化名)和其女友劉穎康辑,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體轿亮,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡疮薇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了我注。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片按咒。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖但骨,靈堂內的尸體忽然破棺而出励七,到底是詐尸還是另有隱情,我是刑警寧澤嗽冒,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布呀伙,位于F島的核電站,受9級特大地震影響添坊,放射性物質發(fā)生泄漏剿另。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雨女。 院中可真熱鬧谚攒,春花似錦、人聲如沸氛堕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽讼稚。三九已至括儒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锐想,已是汗流浹背帮寻。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赠摇,地道東北人固逗。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像藕帜,于是被迫代替她去往敵國和親烫罩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • Activity的啟動速度是很多開發(fā)者關心的問題洽故,當頁面跳轉耗時過長時贝攒,App就會給人一種非常笨重的感覺。在遇到某...
    ModestStorm閱讀 1,331評論 0 0
  • 1 LeakCanary簡介 LeakCanary 是 Square 公司的一個開源庫时甚。通過它可以在 App 運行...
    Joker_Wan閱讀 351評論 0 2
  • LeakCanary是一個檢測內存泄漏的工具饿这,使用非常簡單。主要用來檢測Activity和Fragment內存泄漏...
    三木仔閱讀 999評論 1 2
  • 使用 LeakCanary 已經有了最新的2.0 版本撞秋,可以去獲取最新版本https://github.com/s...
    莫庫施勒閱讀 828評論 0 1
  • 最近在準備android面試,整理了下相關的面試題嚣鄙,分為如下三個部分:android部分吻贿、Java部分、算法面試題...
    JasmineBen閱讀 7,085評論 10 137