源碼淺析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 僅需引入依賴即可完成初始化,放到現(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ì)分析
) {
// 檢查是否在主線程
// 原理: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 = {
// 切換到主線程延遲執(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 別忘了,就是切換到主線程延遲 5s 執(zhí)行
checkRetainedExecutor.execute {
// 主線程調(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 這里是放到子線程執(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è)主線程
// ...
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-06
android實(shí)現(xiàn)緩存圖片等數(shù)據(jù)
本文給大家分享的是Android采用LinkedHashMap自帶的LRU 算法緩存數(shù)據(jù)的方法和示例,有需要的小伙伴可以參考下。2015-07-07
Android自定義半圓形圓盤(pán)滾動(dòng)選擇器
這篇文章主要為大家詳細(xì)介紹了Android自定義半圓形圓盤(pán)滾動(dòng)選擇器 ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)單點(diǎn)觸摸移動(dòng)圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解
這篇文章主要為大家介紹了Android性能圖論在啟動(dòng)優(yōu)化中的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Android檢測(cè)url地址是否可達(dá)的兩種方法
今天小編就為大家分享一篇Android檢測(cè)url地址是否可達(dá)的兩種方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
android仿微信表情雨下落效果的實(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-09
android自定義Camera實(shí)現(xiàn)錄像和拍照
這篇文章主要為大家詳細(xì)介紹了android自定義Camera實(shí)現(xiàn)錄像和拍照功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05

