LeakCanary2.0版本原理簡單查看

這幾天看了一下leakCanary2.0版本的源碼,在這裏做一下記錄。
2.0版本使用kotlin重寫的,使用起來也非常簡單,省去了在Application中的註冊,只需要在build.gradle文件中加入依賴

    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'

就可以了,代碼上什麼都不用寫,完全做到了無感知操作。

那麼代碼上什麼都不用寫,不用註冊它怎麼來進行調用,進行內存監測呢?
答案就在AppWatcherInstaller這個類上,它繼承了ContentProvider,屬於四大金剛,啊 不,四大組件之一。contentProvider的特點就是不用顯示調用初始化,在執行完application的初始化後就會調用contentProvider的onCreate()方法。正是利用這一點,leakcanary把註冊寫在了這裏面,有系統自動調用完成,對開發者完全無感知。
初次看leakCananry源碼,我分了兩部分來看的:1、添加要觀察的對象 2、對對象進行觀察
第一部分,添加觀察對象
在AppWatcherInstaller的onCreate中調用了InternalAppWatcher的install方法
1、檢查是否是在主線程(不是的話拋出異常)
2、添加生命週期的回調,在執行onDestory的時候調用objectWatcher的watch方法,觀察這個activity的對象是否回收掉了

     ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//註冊fragment的watcher
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)

//ActivityDestroyWatcher裏的部分代碼,再執行onDestory的時候開始進行對象檢測

    private val lifecycleCallbacks =
       object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
  override fun onActivityDestroyed(activity: Activity) {
    if (configProvider().watchActivities) {
      objectWatcher.watch(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
      )
    }
  }
}

以上爲註冊流程。
第二部分就是檢測對象的回收了
檢測對象的回收 起始類是ObjectWatcher,顧名思義對象觀察器。調用watch方法。ObjectWatcher.watch(watchedObject : Any,description : String)。watch方法裏面做了如下操作:
在這個之前要先介紹一下,ObjectWatcher裏面有兩個集合分別是:
1) private val watchedObjects = mutableMapOf<String, KeyedWeakReference>() 這是一個map集合,用來存放要觀察對象的key和弱引用,代碼會爲每個觀察的對象生成一個唯一的key和弱應用
2)private val queue = ReferenceQueue()watchedObjects 這個隊列和弱應用聯合使用,當弱引用中的對象被回收後,這個弱引用會被放到這個隊列中。換句話說就是隻要存在這個隊列中弱引用,就代表這個弱引用中所包含的對象被回收了。
下面開始watch方法裏面的操作。
1、先移除watchedObjects 和queue 集合裏面已經回收對象的弱引用。
2、通過uuid爲當前觀察的對象生成一個唯一的key,並把對象用弱應用包起來,放到watchedObjects 這個集合中,同時把queue 和弱應用關聯起來。
3、checkRetainedExecutor.execute {moveToRetained(key) }這裏是個延遲任務,延遲五秒後再次執行1操作,這個期間對象可能已經被回收了,所以需要再次移除一次
4、執行了3操作後,就可以通過watchedObjects 集合找到沒有被回收的對象了。這時候就可以獲取到沒有被回收的對象的個數,大於0,進行一次gc操作(gc操作用的是Runtime.getRuntime() .gc(),源碼上註釋這個操作比system.gc()更可能觸發gc操作)。

 var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
  gcTrigger.runGc()
  retainedReferenceCount = objectWatcher.retainedObjectCount
}

5、再次獲取未被回收的個數,這一步會有幾個判斷條件
1)如果個數小於5,不做操作等待5秒再次進行檢查未回收的個數,一直循環,直到大於等於5個或者等於0個,爲了防止頻發回收堆造成卡頓。
2)大於5個後,如果處於debug模式,會再等20秒,再次執行4操作。防止debug模式會減慢回收
3)距離上次堆棧分析是否大於等於1分鐘,如果沒有超過一分鐘,也需要再次延遲(1分鐘-當前距離上次的時間)再次循環4操作

6、如果上面的條件都符合了,就可以開始進行堆棧的分析了
1)、獲取到內容文件 Debug.dumpHprofData(heapDumpFile.absolutePath)
2)、objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)該操作是去掉以前已經分析過的對象,也就是去除掉之前的沒有回收掉的對象,不在本次分析範圍內
3)、HeapAnalyzerService開啓IntentService服務進行分析 具體分析就不寫了,因爲還沒看懂
4)、把結果插入數據庫(泄漏區分了應用本身的內存泄漏和類庫的內存泄漏),並且發送通知

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章