這幾天看了一下leakCanary2.0版本的源碼变勇,在這里做一下記錄禀苦。
2.0版本使用kotlin重寫的,使用起來也非常簡單遭铺,省去了在Application中的注冊丽柿,只需要在build.gradle文件中加入依賴
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'
就可以了,代碼上什么都不用寫掂僵,完全做到了無感知操作航厚。
那么代碼上什么都不用寫,不用注冊它怎么來進行調(diào)用锰蓬,進行內(nèi)存監(jiān)測呢幔睬?
答案就在AppWatcherInstaller這個類上,它繼承了ContentProvider芹扭,屬于四大金剛麻顶,啊 不,四大組件之一舱卡。contentProvider的特點就是不用顯示調(diào)用初始化辅肾,在執(zhí)行完application的初始化后就會調(diào)用contentProvider的onCreate()方法。正是利用這一點轮锥,leakcanary把注冊寫在了這里面矫钓,有系統(tǒng)自動調(diào)用完成,對開發(fā)者完全無感知。
初次看leakCananry源碼新娜,我分了兩部分來看的:1赵辕、添加要觀察的對象 2、對對象進行觀察
第一部分概龄,添加觀察對象
在AppWatcherInstaller的onCreate中調(diào)用了InternalAppWatcher的install方法
1还惠、檢查是否是在主線程(不是的話拋出異常)
2、添加生命周期的回調(diào)私杜,在執(zhí)行onDestory的時候調(diào)用objectWatcher的watch方法蚕键,觀察這個activity的對象是否回收掉了
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//注冊fragment的watcher
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
//ActivityDestroyWatcher里的部分代碼,再執(zhí)行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,顧名思義對象觀察器咬崔。調(diào)用watch方法粱锐。ObjectWatcher.watch(watchedObject : Any,description : String)拟逮。watch方法里面做了如下操作:
在這個之前要先介紹一下,ObjectWatcher里面有兩個集合分別是:
1) private val watchedObjects = mutableMapOf<String, KeyedWeakReference>() 這是一個map集合达皿,用來存放要觀察對象的key和弱引用,代碼會為每個觀察的對象生成一個唯一的key和弱應用
2)private val queue = ReferenceQueue<Any>()watchedObjects 這個隊列和弱應用聯(lián)合使用叶洞,當弱引用中的對象被回收后,這個弱引用會被放到這個隊列中田篇。換句話說就是只要存在這個隊列中弱引用,就代表這個弱引用中所包含的對象被回收了箍铭。
下面開始watch方法里面的操作泊柬。
1、先移除watchedObjects 和queue 集合里面已經(jīng)回收對象的弱引用诈火。
2兽赁、通過uuid為當前觀察的對象生成一個唯一的key,并把對象用弱應用包起來,放到watchedObjects 這個集合中冷守,同時把queue 和弱應用關聯(lián)起來刀崖。
3、checkRetainedExecutor.execute {moveToRetained(key) }這里是個延遲任務拍摇,延遲五秒后再次執(zhí)行1操作亮钦,這個期間對象可能已經(jīng)被回收了,所以需要再次移除一次
4充活、執(zhí)行了3操作后蜂莉,就可以通過watchedObjects 集合找到?jīng)]有被回收的對象了。這時候就可以獲取到?jīng)]有被回收的對象的個數(shù)混卵,大于0映穗,進行一次gc操作(gc操作用的是Runtime.getRuntime() .gc(),源碼上注釋這個操作比system.gc()更可能觸發(fā)gc操作)幕随。
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
5蚁滋、再次獲取未被回收的個數(shù),這一步會有幾個判斷條件
1)如果個數(shù)小于5,不做操作等待5秒再次進行檢查未回收的個數(shù),一直循環(huán)辕录,直到大于等于5個或者等于0個澄阳,為了防止頻發(fā)回收堆造成卡頓。
2)大于5個后踏拜,如果處于debug模式碎赢,會再等20秒,再次執(zhí)行4操作速梗。防止debug模式會減慢回收
3)距離上次堆棧分析是否大于等于1分鐘肮塞,如果沒有超過一分鐘,也需要再次延遲(1分鐘-當前距離上次的時間)再次循環(huán)4操作
6姻锁、如果上面的條件都符合了枕赵,就可以開始進行堆棧的分析了
1)、獲取到內(nèi)容文件 Debug.dumpHprofData(heapDumpFile.absolutePath)
2)位隶、objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)該操作是去掉以前已經(jīng)分析過的對象拷窜,也就是去除掉之前的沒有回收掉的對象,不在本次分析范圍內(nèi)
3)涧黄、HeapAnalyzerService開啟IntentService服務進行分析 具體分析就不寫了篮昧,因為還沒看懂
4)、把結果插入數(shù)據(jù)庫(泄漏區(qū)分了應用本身的內(nèi)存泄漏和類庫的內(nèi)存泄漏)笋妥,并且發(fā)送通知