源碼淺析Android中內(nèi)存泄漏檢測(cè)工具Leakcanary的使用
前言
本博客將分析一下大名鼎鼎的 Leakcanary 想必作為 Android 開(kāi)發(fā)都多多少少接觸過(guò),新版本的 Leakcanary 也用 Kotlin 重寫(xiě)了一遍,最近詳細(xì)查看了下源碼,分享一下。
tips:本來(lái)是只想分析下內(nèi)存泄漏檢測(cè)部分,但寫(xiě)著寫(xiě)著就跑偏了,因?yàn)閮?nèi)存泄漏的檢測(cè)難點(diǎn)在于對(duì)對(duì)象生命周期的把控, Leakcanary 對(duì)于 Service 生命周期的把控我覺(jué)得非常值得我們學(xué)習(xí),并且在項(xiàng)目中也會(huì)用到。外加 Leakcanary 用 Kotlin 重寫(xiě),一些語(yǔ)法糖我平時(shí)也沒(méi)用過(guò),就順便寫(xiě)了下,整體讀下來(lái)有點(diǎn)啰嗦。
源碼版本
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
內(nèi)存泄漏
本博客著重分析 leakcanary 源碼實(shí)現(xiàn)原理以及一些優(yōu)秀設(shè)計(jì),對(duì)于內(nèi)存泄漏的解釋就簡(jiǎn)單用我自己的理解來(lái)解釋下:短生命周期對(duì)象持有長(zhǎng)生命周期對(duì)象,當(dāng)短生命周期對(duì)象需要被回收時(shí)因其持有長(zhǎng)生命周期對(duì)象導(dǎo)致無(wú)法正?;厥盏那闆r;
源碼淺析
在了解其優(yōu)秀設(shè)計(jì)之前先來(lái)簡(jiǎn)單分析下其源碼以及實(shí)現(xiàn)原理。
檢測(cè)原理
Leakcanary 檢測(cè)內(nèi)存泄漏的原理很簡(jiǎn)單,就是利用弱引用 WeakReference 的雙參數(shù)構(gòu)造方法
WeakReference(T referent, ReferenceQueue<? super T> q)
來(lái)檢測(cè)被弱引用的對(duì)象是否被正?;厥铡⑨尫?,舉個(gè)例子:
// 定義類(lèi) class A // 檢測(cè)的目標(biāo)對(duì)象 val obj = A() val queue = ReferenceQueue<A>() val weakObj = WeakReference(obj, queue) // 觸發(fā)gc回收(注意:這樣的操作不一定可以觸發(fā)gc,具體如何觸發(fā)gc 在下面的源碼分析中有提到 leakcanary 是如何觸發(fā)gc的) System.gc() val tmp = queue.poll() if (tmp === obj) { // 被回收 } else { // 未回收 }
Android 開(kāi)發(fā)中的 Activity、Fragment、Service、自定義 View 都是容易發(fā)生內(nèi)存泄漏的對(duì)象,Leakcanary 所做的工作就是在合適的時(shí)機(jī)(一般是在回收時(shí),如 Activity 的 onDestory 后)對(duì)這些對(duì)象進(jìn)行弱引用并且關(guān)聯(lián)引用隊(duì)列,根據(jù)其是否被添加到引用隊(duì)列來(lái)判斷是否發(fā)生泄漏。
關(guān)于判斷一個(gè)對(duì)象是否發(fā)生泄漏的原理上面的示例代碼已經(jīng)簡(jiǎn)單演示,下面我們就順著源碼來(lái)看看 Leakcanary 的實(shí)現(xiàn)細(xì)節(jié)。
初始化
Leakcanary 僅需引入依賴(lài)即可完成初始化,放到現(xiàn)在這也不算多么神奇的技巧了,這是利用了 ContentProvider。
ContentProvider 的初始化時(shí)機(jī)在 Application 的 onCreate 之前,并且在 ContentProvider 的 onCreate 方法中可以獲取到 context、applicationContext。
當(dāng)項(xiàng)目引入 Leakcanary 后打包出的 apk 的清單文件中可以找到注冊(cè)了MainProcessAppWatcherInstaller
,其關(guān)鍵源碼部分如下:
internal class MainProcessAppWatcherInstaller : ContentProvider() { override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppWatcher.manualInstall(application) return true } //.. }
可以看出調(diào)用了 AppWatcher.manualInstall()
進(jìn)行了初始化,其源碼如下:
AppWatcher.kt
fun manualInstall( application: Application, retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默認(rèn) 5s watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) // 獲取默認(rèn)Watchers 下面詳細(xì)分析 ) { // 檢查是否在主線(xiàn)程 // 原理:Looper.getMainLooper().thread === Thread.currentThread() checkMainThread() // ... this.retainedDelayMillis = retainedDelayMillis // 初始化 Shark 庫(kù) if (application.isDebuggableBuild) { LogcatSharkLog.install() } // 這行代碼下面詳細(xì)分析 LeakCanaryDelegate.loadLeakCanary(application) // 對(duì) watchers 遍歷調(diào)用 install watchersToInstall.forEach { it.install() } // 給 installCause 賦值,代表已經(jīng)初始化 installCause = RuntimeException("manualInstall() first called here") }
appDefaultWatchers(application)
上述初始化方法中第三個(gè)參數(shù) watchersToInstall
被賦予了默認(rèn)值,通過(guò) appDefaultWatchers
獲取了一個(gè) List<InstallableWatcher>
,先看下 InstallableWatcher 源碼:
interface InstallableWatcher { fun install() fun uninstall() }
是一個(gè)接口,定義了兩個(gè)方法看命名也能明白是安裝和卸載,接著看下 appDefaultWatchers 方法返回了什么:
fun appDefaultWatchers( application: Application, reachabilityWatcher: ReachabilityWatcher = objectWatcher // 注意這個(gè) objectWatcher 很重要 ): List<InstallableWatcher> { return listOf( ActivityWatcher(application, reachabilityWatcher), // 用于監(jiān)控activity內(nèi)存泄漏 FragmentAndViewModelWatcher(application, reachabilityWatcher),// 用于監(jiān)控fragment,viewmodel 內(nèi)存泄漏 RootViewWatcher(reachabilityWatcher),// 用于監(jiān)聽(tīng) rootview 內(nèi)存泄漏 ServiceWatcher(reachabilityWatcher) // 用于監(jiān)聽(tīng) service 內(nèi)存泄漏 ) }
注意上述方法的第二個(gè)參數(shù) reachabilityWatcher
默認(rèn)賦值了 objectWatcher
:
val objectWatcher = ObjectWatcher(...)
先記住他是 ObjectWatcher 類(lèi)的實(shí)例,并且將其傳遞給了用于檢測(cè)的各個(gè) InstallableWatcher 實(shí)現(xiàn)類(lèi)。
LeakCanaryDelegate.loadLeakCanary(application)
接著再回過(guò)頭來(lái)看一下 LeakCanaryDelegate.loadLeakCanary(application)
這句代碼,loadLeakCanary 作為 LeakCanaryDelegate 類(lèi)中的一個(gè)函數(shù)類(lèi)型變量,所以可以直接調(diào)用,看一下其源碼:
internal object LeakCanaryDelegate { // 延遲初始化 val loadLeakCanary by lazy { try { // 默認(rèn)加載 InternalLeakCanary 獲取其 INSTANCE 字段 // InternalLeakCanary 是一個(gè) object class,編譯為 java 后會(huì)自動(dòng)生成 INSTANCE // 就是一個(gè)單例類(lèi) 這里是獲取其單例對(duì)象 val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary") leakCanaryListener.getDeclaredField("INSTANCE") .get(null) as (Application) -> Unit } catch (ignored: Throwable) { // 出現(xiàn)異常時(shí)返回 NoLeakCanary NoLeakCanary } } // (Application) -> Unit 函數(shù)類(lèi)型變量,接受一個(gè) application 作為參數(shù) // 內(nèi)部都是空實(shí)現(xiàn) object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener { override fun invoke(application: Application) {} override fun onObjectRetained() {} } }
一般情況下 LeakCanaryDelegate.loadLeakCanary(application) 就相當(dāng)于調(diào)用了 InternalLeakCanary,當(dāng)然 InternalLeakCanary 也是 (Application) -> Unit 的實(shí)現(xiàn)了,對(duì)你沒(méi)看錯(cuò),函數(shù)類(lèi)型不僅可以聲明變量,也可以定義實(shí)現(xiàn)類(lèi),但需要實(shí)現(xiàn) invoke
方法,看下其源碼:
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener { override fun invoke(application: Application) { _application = application checkRunningInDebuggableBuild() // 檢查是否是 debug 模式 // 對(duì) AppWatcher 的 objectWatcher 添加 listener AppWatcher.objectWatcher.addOnObjectRetainedListener(this) // 這個(gè)用于觸發(fā) gc 回收,下面會(huì)分析一下 val gcTrigger = GcTrigger.Default // 獲取相關(guān)配置 val configProvider = { LeakCanary.config } // 啟動(dòng)了一個(gè) HandlerThread val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME) handlerThread.start() val backgroundHandler = Handler(handlerThread.looper) // 初始化堆轉(zhuǎn)儲(chǔ)對(duì)象,上面定義的一些變量也都傳入了進(jìn)去 heapDumpTrigger = HeapDumpTrigger( application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, configProvider ) // Leakcanary 對(duì) Application 的一個(gè)擴(kuò)展方法 // 這里的作用是在 App 前后臺(tái)切換時(shí)執(zhí)行閉包中的邏輯 也就是調(diào)用堆轉(zhuǎn)儲(chǔ)的 onApplicationVisibilityChanged 方法 application.registerVisibilityListener { applicationVisible -> this.applicationVisible = applicationVisible heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible) } // 這個(gè)方法代碼比較簡(jiǎn)單利用 Application.registerActivityLifecycleCallbacks // 獲取處于激活狀態(tài)的 Activity 賦值給 resumedActivity registerResumedActivityListener(application) // 添加桌面快捷方式,也就是Leakcanary小黃鳥(niǎo)app圖標(biāo),感興趣的可以看一下 這里就不詳細(xì)分析這個(gè)方法了 addDynamicShortcut(application) // ... } }
實(shí)現(xiàn)細(xì)節(jié)
從初始化部分源碼中可以看出對(duì)于內(nèi)存泄漏監(jiān)控的核心部分就是 appDefaultWatchers 方法中返回的四個(gè)InstallableWatcher 的實(shí)現(xiàn)類(lèi),下面我們依次來(lái)分析。
ObjectWatcher
在上述分析中四個(gè)實(shí)現(xiàn)類(lèi)在初始化時(shí)都傳入了 objectWatcher,所以有必要先來(lái)初步了解下:
AppWatcher.kt
val objectWatcher = ObjectWatcher( // 返回一個(gè) Clock 對(duì)象 clock = { SystemClock.uptimeMillis() }, // Executor 對(duì)象 checkRetainedExecutor = { // 切換到主線(xiàn)程延遲執(zhí)行, retainedDelayMillis 默認(rèn)是 5s mainHandler.postDelayed(it, retainedDelayMillis) }, // 這個(gè)參數(shù)是 () -> Boolean 函數(shù)類(lèi)型,默認(rèn)是直接返回一個(gè) true isEnabled = { true } )
我第一次看到這個(gè)clock = { SystemClock.uptimeMillis() }
是有點(diǎn)沒(méi)看懂,所以先來(lái)分析下這個(gè)寫(xiě)法,先看看這個(gè) Clock 源碼:
// 注意這個(gè) fun interface 是 kotlin 在 1.4 引入的 函數(shù)式接口 // 接口中只能有一個(gè)未實(shí)現(xiàn)的方法 fun interface Clock { fun uptimeMillis(): Long // ... }
clock = { SystemClock.uptimeMillis() }
就等價(jià)于以下代碼:
clock = object : Clock{ override fun uptimeMillis(): Long { SystemClock.uptimeMillis() } }
大概先了解下 objectWatcher 的參數(shù)部分,具體方法待下面用到時(shí)具體分析。
ActivityWatcher
先來(lái)看看我們最熟的 Activity 是如何進(jìn)行內(nèi)存泄漏監(jiān)控的,ActivityWatcher 源碼如下:
class ActivityWatcher( private val application: Application, // 注意這里,傳進(jìn)來(lái)的是 AppWatcher 的 objectWatcher private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityDestroyed(activity: Activity) { // 核心就在這一行代碼里 這里也就是調(diào)用了 objectWatcher 的 expectWeaklyReachable 方法 reachabilityWatcher.expectWeaklyReachable( activity, "${activity::class.java.name} received Activity#onDestroy() callback" ) } } override fun install() { // 利用 Application 監(jiān)聽(tīng) Activity onDestory 生命周期方法 application.registerActivityLifecycleCallbacks(lifecycleCallbacks) } override fun uninstall() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) } }
直接查看 ObjectWatcher 的 expectWeaklyReachable 方法源碼:
ObjectWatcher.kt
// 保存監(jiān)控對(duì)象的 map private val watchedObjects = mutableMapOf<String, KeyedWeakReference>() // 用于弱引用關(guān)聯(lián)隊(duì)列 private val queue = ReferenceQueue<Any>() @Synchronized override fun expectWeaklyReachable( watchedObject: Any, // 傳入的 activity(已經(jīng) onDestory) description: String // 描述 ) { if (!isEnabled()) { // 默認(rèn) isEnabled 返回 true,不會(huì)進(jìn)入這個(gè) if return } // 這個(gè)代碼比較簡(jiǎn)單 我就簡(jiǎn)單說(shuō)下作用 // 當(dāng) watchedObject 成功被回收會(huì)添加進(jìn) queue 中,這個(gè)方法就是將添加到 queue 中的對(duì)象 // 從 watchedObjects 中移除,被移除的說(shuō)明沒(méi)有發(fā)生內(nèi)存泄漏 removeWeaklyReachableObjects() // 生成id (相當(dāng)于對(duì)象的身份證) val key = UUID.randomUUID() .toString() // 獲取時(shí)間戳 val watchUptimeMillis = clock.uptimeMillis() // 將對(duì)象以及一些信息包裝為 KeyedWeakReference val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) // ... // 包裝好的對(duì)象放入 watchedObjects 這個(gè) map 中 watchedObjects[key] = reference // checkRetainedExecutor 別忘了,就是切換到主線(xiàn)程延遲 5s 執(zhí)行 checkRetainedExecutor.execute { // 主線(xiàn)程調(diào)用 moveToRetained(key) } }
KeyedWeakReference
class KeyedWeakReference( referent: Any, // 監(jiān)控對(duì)象,這里是 onDestory 的 Activity val key: String, // UUID 身份證 val description: String, // 描述 val watchUptimeMillis: Long, // 時(shí)間 referenceQueue: ReferenceQueue<Any> // 當(dāng) referent 被回收會(huì)添加到這個(gè)隊(duì)列中 ) : WeakReference<Any>(referent, referenceQueue) //最后調(diào)用了 WeakReference 的雙參數(shù)構(gòu)造器
這個(gè)包裝相當(dāng)于對(duì) WeakReference 做了一些擴(kuò)展,可以通過(guò) key 從前面的 watchedObjects 中移除或者獲取。
moveToRetained
再來(lái)看看 moveToRetained 方法源碼:
@Synchronized private fun moveToRetained(key: String) { // 再次嘗試從 queue 中獲取回收成功的對(duì)象,從 watcherObjects 中移除 removeWeaklyReachableObjects() val retainedRef = watchedObjects[key] if (retainedRef != null) { // 如果還可以獲取到代表回收失敗 發(fā)生了內(nèi)存泄漏 retainedRef.retainedUptimeMillis = clock.uptimeMillis() // 遍歷 onObjectRetainedListeners 對(duì)象調(diào)用其 onObjectRetained 方法 onObjectRetainedListeners.forEach { it.onObjectRetained() } } }
注意這個(gè) onObjectRetainedListeners,在初始化的第二小節(jié)我們分析的 InternalLeakCanary 源碼中有一處調(diào)用: AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
這里的遍歷調(diào)用一般情況下就相當(dāng)于調(diào)用了 AppWatcher 的 onObjectRetained 方法,看下其源碼部分:
InternalLeakCanary.kt
// 又調(diào)用了 scheduleRetainedObjectCheck 方法 override fun onObjectRetained() = scheduleRetainedObjectCheck() fun scheduleRetainedObjectCheck() { if (this::heapDumpTrigger.isInitialized) { // 又調(diào)用到了 堆轉(zhuǎn)儲(chǔ) 對(duì)象里的 scheduleRetainedObjectCheck 方法 heapDumpTrigger.scheduleRetainedObjectCheck() } }
繼續(xù)跟蹤源碼,查看 HeapDumpTrigger 類(lèi)中的 scheduleRetainedObjectCheck 方法:
HeapDumpTrigger.kt
fun scheduleRetainedObjectCheck( delayMillis: Long = 0L ) { val checkCurrentlyScheduledAt = checkScheduledAt if (checkCurrentlyScheduledAt > 0) { return } checkScheduledAt = SystemClock.uptimeMillis() + delayMillis // backgroundHandler 前面看過(guò)了 是一個(gè) HandlerThread 這里是放到子線(xiàn)程執(zhí)行 backgroundHandler.postDelayed({ checkScheduledAt = 0 // 重點(diǎn)是這個(gè)方法 checkRetainedObjects() }, delayMillis) }
接著查看 checkRetainedObjects 方法源碼:
private fun checkRetainedObjects() { val iCanHasHeap = HeapDumpControl.iCanHasHeap() val config = configProvider() // ... // 獲取仍然存在于 map 中沒(méi)有被回收的對(duì)象數(shù)量 var retainedReferenceCount = objectWatcher.retainedObjectCount // 如果有再執(zhí)行一次 gc if (retainedReferenceCount > 0) { gcTrigger.runGc() // gc 操作,注意這里的實(shí)現(xiàn) 后面會(huì)貼源碼 retainedReferenceCount = objectWatcher.retainedObjectCount // 再次獲取數(shù)量 } // 里面是對(duì)app 是否在前臺(tái),存活對(duì)象數(shù)量的判斷,retainedVisibleThreshold 默認(rèn)是 5 if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return val now = SystemClock.uptimeMillis() val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis // 判斷上次堆轉(zhuǎn)儲(chǔ)時(shí)間間隔是否小于 60s if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) { onRetainInstanceListener.onEvent(DumpHappenedRecently) showRetainedCountNotification( objectCount = retainedReferenceCount, contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait) ) // 小于60s 就延遲對(duì)應(yīng)時(shí)間在重新執(zhí)行該方法 scheduleRetainedObjectCheck( delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis ) return } dismissRetainedCountNotification() val visibility = if (applicationVisible) "visible" else "not visible" // 開(kāi)始堆轉(zhuǎn)儲(chǔ) dumpHeap( retainedReferenceCount = retainedReferenceCount, retry = true, reason = "$retainedReferenceCount retained objects, app is $visibility" ) }
注意上述的 gcTrigger.runGc()
觸發(fā) gc 操作,這段代碼看一下 Leakcanary 是如何實(shí)現(xiàn)的:
object Default : GcTrigger { override fun runGc() { // 這段執(zhí)行g(shù)c的代碼 leakcanary 注釋中表示是借鑒于 Android 系統(tǒng)源碼片段 // System.gc() 并不是每次執(zhí)行都能觸發(fā)gc,而 Runtime gc() 更容易觸發(fā) // 這里的源碼細(xì)節(jié)值得借鑒 Runtime.getRuntime().gc() enqueueReferences() System.runFinalization() } // ... }
到這里源碼還剩下兩個(gè)部分:1. 獲取堆信息;2. 分析堆信息;先說(shuō)下第二點(diǎn) Leakcanary 在 2.x 之前的版本使用 haha 這個(gè)庫(kù)來(lái)解析 .hprof 文件,而 2.x 之后另起爐灶用 shark 庫(kù)來(lái)完成這一工作,很遺憾我對(duì) shark 的庫(kù)不夠了解,這部分就暫時(shí)不深入分析了。
來(lái)看看 Leakcanary 是如何獲取堆信息寫(xiě)入文件的,dumpHeap 方法源碼:
private fun dumpHeap( retainedReferenceCount: Int, retry: Boolean, reason: String ) { // 注意這個(gè) directoryProvider 并不是 ContentProvider 只是一個(gè)普通類(lèi) val directoryProvider = InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application) // 新建堆轉(zhuǎn)儲(chǔ)文件,內(nèi)部包含一些權(quán)限、目錄等等邏輯判斷 比較簡(jiǎn)單就不貼代碼了 // 生成文件名:fileName = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS'.hprof'", Locale.US).format(Date()) val heapDumpFile = directoryProvider.newHeapDumpFile() val durationMillis: Long if (currentEventUniqueId == null) { currentEventUniqueId = UUID.randomUUID().toString() } try { // 通知欄 Event InternalLeakCanary.sendEvent(DumpingHeap(currentEventUniqueId!!)) // ... durationMillis = measureDurationMillis { // 這里是堆轉(zhuǎn)儲(chǔ)核心 configProvider().heapDumper.dumpHeap(heapDumpFile) } // 觸發(fā)后面的分析方法 InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason)) } catch (throwable: Throwable) { // 堆轉(zhuǎn)儲(chǔ)失敗 InternalLeakCanary.sendEvent(HeapDumpFailed(currentEventUniqueId!!, throwable, retry)) if (retry) { // 重試則延遲再執(zhí)行一次方法 scheduleRetainedObjectCheck( delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS ) } // 通知欄展示 showRetainedCountNotification( objectCount = retainedReferenceCount, contentText = application.getString( R.string.leak_canary_notification_retained_dump_failed ) ) return } }
configProvider().heapDumper.dumpHeap(heapDumpFile)
這行代碼是堆轉(zhuǎn)儲(chǔ)的核心,來(lái)看一下其源碼:
AndroidDebugHeapDumper.kt
object AndroidDebugHeapDumper : HeapDumper { override fun dumpHeap(heapDumpFile: File) { // 利用 Android 本身的 Debug dumpHprofData 方法實(shí)現(xiàn) Debug.dumpHprofData(heapDumpFile.absolutePath) } }
堆轉(zhuǎn)儲(chǔ)的實(shí)現(xiàn)非常簡(jiǎn)單,系統(tǒng)源碼中自帶了對(duì)應(yīng)方法,傳入文件即可。
FragmentAndViewModelWatcher
接著來(lái)看看對(duì)于 Fragment 和 ViewModel 是如何進(jìn)行檢測(cè)的,直接看源碼:
和 ActivityWatcher 如出一轍,同樣利用 Application 的 api 注冊(cè)對(duì) Activity 的監(jiān)聽(tīng),在 Activity onCreate 生命周期時(shí)遍歷 fragmentDestoryWatchers 進(jìn)行調(diào)用,接著來(lái)看看 fragmentDestoryWatchers 的定義:
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run { // 注意這個(gè) list 的類(lèi)型,item 是函數(shù)式變量,相當(dāng)于方法可以直接調(diào)用 val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>() // sdk26 以上添加 AndroidOFragmentDestroyWatcher if (SDK_INT >= O) { fragmentDestroyWatchers.add( // reachabilityWatcher 是老朋友 objectWatcher 了 AndroidOFragmentDestroyWatcher(reachabilityWatcher) ) } // 通過(guò)反射獲取 AndroidXFragmentDestroyWatcher // 對(duì)應(yīng) androidX 的 fragment getWatcherIfAvailable( ANDROIDX_FRAGMENT_CLASS_NAME, // "androidx.fragment.app.Fragment" ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, // "AndroidXFragmentDestroyWatcher" reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } // 通過(guò)反射獲取 AndroidSupportFragmentDestroyWatcher // 對(duì)應(yīng)之前的 android support 包下的 Fragment getWatcherIfAvailable( ANDROID_SUPPORT_FRAGMENT_CLASS_NAME, // "android.support.v4.app.Fragment" ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, // "AndroidSupportFragmentDestroyWatcher" reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } // 返回 fragmentDestroyWatchers }
依次來(lái)看看上面定義的三個(gè) item 實(shí)現(xiàn)細(xì)節(jié)。
AndroidOFragmentDestroyWatcher
reachabilityWatcher.expectWeaklyReachable()
方法在 ActiviyWatcher 小節(jié)中已經(jīng)詳細(xì)分析了,就不再贅述了。
AndroidXFragmentDestroyWatcher
和 AndroidOFragmentDestroyWatcher 一樣是通過(guò) Activity 來(lái)監(jiān)聽(tīng) Fragment 的生命周期,差別在于注冊(cè)監(jiān)聽(tīng)的 api :
internal class AndroidXFragmentDestroyWatcher( private val reachabilityWatcher: ReachabilityWatcher ) : (Activity) -> Unit { // 和上面的 AndroidOFragmentDestroyWatcher 一樣 就不貼代碼了 private val fragmentLifecycleCallbacks = // ... override fun invoke(activity: Activity) { if (activity is FragmentActivity) { // 和 AndroidOFragmentDestroyWatcher 的原理是一樣的 val supportFragmentManager = activity.supportFragmentManager supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true) // 注意這里,多了一個(gè)針對(duì) ViewModel 的處理 ViewModelClearedWatcher.install(activity, reachabilityWatcher) } } }
這里增加了對(duì) ViewModel 的監(jiān)聽(tīng),看一下其是如何實(shí)現(xiàn)的。
ViewModelClearedWatcher
直接看源碼:
// 注意這是一個(gè) ViewModel 的子類(lèi) internal class ViewModelClearedWatcher( storeOwner: ViewModelStoreOwner, private val reachabilityWatcher: ReachabilityWatcher ) : ViewModel() { companion object { // 外部調(diào)用的 install 方法 fun install( storeOwner: ViewModelStoreOwner, // 這里也就是 activity reachabilityWatcher: ReachabilityWatcher // objectWatcher ) { // 相當(dāng)于再 Activity onCreate 方法中又創(chuàng)建了一個(gè) ViewModel val provider = ViewModelProvider(storeOwner, object : Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create(modelClass: Class<T>): T = ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T }) provider.get(ViewModelClearedWatcher::class.java) } } // 反射 activity 中的 ViewModelStore 中的 mMap 字段 // 這里面存儲(chǔ)著 activity 中創(chuàng)建的 ViewModel private val viewModelMap: Map<String, ViewModel>? = try { val mMapField = ViewModelStore::class.java.getDeclaredField("mMap") mMapField.isAccessible = true mMapField[storeOwner.viewModelStore] as Map<String, ViewModel> } catch (ignored: Exception) { null } // ViewModel 銷(xiāo)毀時(shí)觸發(fā)的方法 override fun onCleared() { viewModelMap?.values?.forEach { viewModel -> // 進(jìn)行檢測(cè) reachabilityWatcher.expectWeaklyReachable( viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback" ) } } }
如果對(duì) ViewModel 這部分不理解的可以看下我之前對(duì)于 ViewModel 源碼分析的博客 ————Android 源碼淺析:Jetpack 組件 —— ViewModel。
AndroidSupportFragmentDestroyWatcher
最后再來(lái)看一下 AndroidSupportFragmentDestroyWatcher,猜也能猜到和上面的邏輯是一樣的,僅僅是為了支持 AndroidSupport 包:
internal class AndroidSupportFragmentDestroyWatcher( private val reachabilityWatcher: ReachabilityWatcher ) : (Activity) -> Unit { // 還是和上面一樣的 private val fragmentLifecycleCallbacks = // ... override fun invoke(activity: Activity) { if (activity is FragmentActivity) { // AndroidSupport 包的 api val supportFragmentManager = activity.supportFragmentManager supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true) } } }
RootViewWatcher
這個(gè)類(lèi)的源碼呢,就不貼了,因?yàn)樯婕暗搅?Square 的另一個(gè)庫(kù) Curtains,這個(gè)庫(kù)的作用就是可以監(jiān)聽(tīng) window 的生命周期。很遺憾我對(duì)這個(gè)庫(kù)的了解也不到位,就不瞎說(shuō)八道了。
不過(guò)這里可以稍微總結(jié)一下,對(duì)于監(jiān)聽(tīng)對(duì)象是否泄漏很關(guān)鍵的一個(gè)點(diǎn)就是能夠監(jiān)聽(tīng)到對(duì)象的生命周期,也就是能監(jiān)聽(tīng)到對(duì)象的銷(xiāo)毀。
下一小節(jié)是值得我們學(xué)習(xí)的一個(gè)小節(jié),監(jiān)聽(tīng) Service 的銷(xiāo)毀,看下 Leakcanary 是如何做到的。
ServiceWatcher (重點(diǎn) 值得學(xué)習(xí)借鑒)
對(duì)于 Service 的生命周期,我們都知道 Service 銷(xiāo)毀時(shí)會(huì)走 onDestory,但是系統(tǒng)并沒(méi)有提供像監(jiān)聽(tīng) Activity 的 Application.registerActivityLifecycleCallbacks 這樣的 api,這一小節(jié)就重點(diǎn)分析下 Leakcanary 是如何做到的。
直接看源碼:
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher { override fun install() { checkMainThread() // 檢測(cè)主線(xiàn)程 // ... try { // 重點(diǎn) 1: 反射 ActivityThread 的 mH 中的 mCallback // 也就是 Handler 的 mCallback swapActivityThreadHandlerCallback { mCallback -> uninstallActivityThreadHandlerCallback = { swapActivityThreadHandlerCallback { mCallback } } Handler.Callback { msg -> if (msg.obj !is IBinder) { return@Callback false } if (msg.what == STOP_SERVICE) { // AMS Service onDestory 的 message 的 what val key = msg.obj as IBinder // 注意這個(gè) activityThreadServices // 這個(gè)是反射獲取的 ActivityThread 中的 mServices 字段是 Map<IBinder, Service> 類(lèi)型 // 這個(gè)字段存放所有啟動(dòng)的 Service activityThreadServices[key]?.let { // 這里是將要銷(xiāo)毀的 Service 進(jìn)行存儲(chǔ) // key 是 IBinder 對(duì)象 onServicePreDestroy(key, it) } } mCallback?.handleMessage(msg) ?: false } } // 重點(diǎn) 2:反射獲取 AMS (ActivityManagerService) swapActivityManager { activityManagerInterface, activityManagerInstance -> uninstallActivityManager = { swapActivityManager { _, _ -> activityManagerInstance } } // 對(duì) AMS 進(jìn)行動(dòng)態(tài)代理 Proxy.newProxyInstance( activityManagerInterface.classLoader, arrayOf(activityManagerInterface) ) { _, method, args -> // 判斷方法名是否是 serviceDoneExecuting if (METHOD_SERVICE_DONE_EXECUTING == method.name) { // 獲取 IBinder 對(duì)象 val token = args!![0] as IBinder // 從上面存儲(chǔ)的 service 容器中查看是否存在對(duì)應(yīng) service if (servicesToBeDestroyed.containsKey(token)) { // 進(jìn)行內(nèi)存泄漏檢測(cè) onServiceDestroyed(token) } } // ... } } } // ... } private fun onServiceDestroyed(token: IBinder) { // 從 servicesToBeDestroyed 中移除 servicesToBeDestroyed.remove(token)?.also { serviceWeakReference -> serviceWeakReference.get()?.let { service -> // 利用 objectWatcher 進(jìn)行內(nèi)存泄漏檢測(cè) reachabilityWatcher.expectWeaklyReachable( service, "${service::class.java.name} received Service#onDestroy() callback" ) } } } }
可以看出對(duì)于 Service 進(jìn)行內(nèi)存泄漏檢測(cè)的核心有兩步驟:
- 反射 Activity 的 mH 的 mCallback 獲取即將銷(xiāo)毀的 Service 對(duì)象;
- 對(duì) AMS 進(jìn)行動(dòng)態(tài)代理監(jiān)聽(tīng) Service 的銷(xiāo)毀時(shí)機(jī)。
總結(jié)
內(nèi)存泄漏檢測(cè)的原理非常簡(jiǎn)單,利用弱引用的雙參數(shù)構(gòu)造傳入引用隊(duì)列,掌握被檢測(cè)對(duì)象的生命周期,在銷(xiāo)毀時(shí)檢查引用隊(duì)列的情況。我們自己也能很輕松的寫(xiě)出對(duì)應(yīng) Demo,但 Leakcanary 中很多優(yōu)秀設(shè)計(jì)非常值得學(xué)習(xí),如:無(wú)感初始化(ContentProvider),一些 Kotlin 語(yǔ)法糖,對(duì)于 Service 生命周期的監(jiān)聽(tīng)。
尤其是最后對(duì)于 Service 生命周期的監(jiān)聽(tīng),通過(guò)反射系統(tǒng)類(lèi),結(jié)合動(dòng)態(tài)代理很輕松就實(shí)現(xiàn)了,由此我們也可以延伸出對(duì) Service 其他生命周期的監(jiān)聽(tīng)。
內(nèi)存泄漏檢測(cè)并不難,難點(diǎn)在于分析堆信息以及對(duì)于各種對(duì)象的生命周期把控。
以上就是源碼淺析Android中內(nèi)存泄漏檢測(cè)工具Leakcanary的使用的詳細(xì)內(nèi)容,更多關(guān)于Android Leakcanary內(nèi)存泄漏檢測(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android網(wǎng)絡(luò)請(qǐng)求庫(kù)android-async-http介紹
這篇文章主要介紹了Android網(wǎng)絡(luò)請(qǐng)求庫(kù)android-async-http介紹,本文講解了android-async-http的概念、特征以及使用實(shí)例,需要的朋友可以參考下2015-06-06android實(shí)現(xiàn)緩存圖片等數(shù)據(jù)
本文給大家分享的是Android采用LinkedHashMap自帶的LRU 算法緩存數(shù)據(jù)的方法和示例,有需要的小伙伴可以參考下。2015-07-07Android自定義半圓形圓盤(pán)滾動(dòng)選擇器
這篇文章主要為大家詳細(xì)介紹了Android自定義半圓形圓盤(pán)滾動(dòng)選擇器 ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解
這篇文章主要為大家介紹了Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Android檢測(cè)url地址是否可達(dá)的兩種方法
今天小編就為大家分享一篇Android檢測(cè)url地址是否可達(dá)的兩種方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01android仿微信表情雨下落效果的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于android仿微信表情雨下落效果的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09android自定義Camera實(shí)現(xiàn)錄像和拍照
這篇文章主要為大家詳細(xì)介紹了android自定義Camera實(shí)現(xiàn)錄像和拍照功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05