LeakCanary使用只需在app中的build.gradle添加依賴
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
沒錯骗卜,一行搞定!
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false"/>
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
//進行初始化
AppWatcher.manualInstall(application)
return true
}
}
apk打包流程中會把這個provider合并到app下的mainfest文件中,ContentProvider的onCreate比Application的onCreate早執(zhí)行左胞,調(diào)用AppWatcher.manualInstall(application)進行初始化的寇仓。
//AppWatcher
fun manualInstall(
application: Application,
//5s,后面checkRetainedExecutor.execute有用到
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
//...
watchersToInstall.forEach {
it.install()
}
}
//初始化4個watcher
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
LeakCanary會Activity烤宙、Fragment遍烦、Fragment的view、ViewModel躺枕、RootView和Service納入檢測服猪。
監(jiān)聽泄漏的時機
ActivityWatcher
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
ActivityWatcher通過registerActivityLifecycleCallbacks監(jiān)聽Activity生命周期回調(diào),在onActivityDestroyed時拐云,調(diào)用objectWatcher.expectWeaklyReachable將Activity納入檢測
FragmentAndViewModelWatcher
兼容了O以上罢猪、AndroidX、Support慨丐,通過fragmentManager.registerFragmentLifecycleCallbacks監(jiān)聽坡脐,在onFragmentViewDestroyed與onFragmentDestroyed中調(diào)用expectWeaklyReachable納入檢測。
對于ViewModel房揭,在AndroidXFragmentDestroyWatcher里還會額外監(jiān)聽
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
反射獲取ViewModelStore的mMap备闲, 在ViewModelClearedWatcher的onCleared中調(diào)用expectWeaklyReachable將ViewModel納入檢測。
RootViewWatcher
通過反射獲取WindowManagerGlobal中的mViews捅暴,再通過addOnAttachStateChangeListener監(jiān)聽rootView恬砂,在onViewDetachedFromWindow時執(zhí)行expectWeaklyReachable納入檢測。
ServiceWatcher
1.反射獲取ActivityThread中的mServices(app中全部Service的一個Map)蓬痒。
2.反射獲取名為H的Handler(Android消息機制中轉(zhuǎn)中心)泻骤。
3.替換H的mCallBack實現(xiàn),當(dāng)消息為STOP_SERVICE時梧奢,便從mServices取出該消息對應(yīng)的Service作為待檢測Service引用狱掂。
4.Hook AMS,通過動態(tài)代理修改它的serviceDoneExecuting方法亲轨,在onServiceDestroyed時執(zhí)行expectWeaklyReachable納入檢測趋惨。
如何檢測內(nèi)存泄漏?
原理:Java中的WeakReference表示弱引用惦蚊,當(dāng)GC時器虾,它所持有的對象如果沒有被其它強引用持有讯嫂,那么它所引用的對象就會被回收,這個WeakReference會被加入到關(guān)聯(lián)的ReferenceQueue兆沙。
最終都是調(diào)用了expectWeaklyReachable納入檢測
//核心代碼片段 ObjectWatcher.kt
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
private val queue = ReferenceQueue<Any>()
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
//遍歷queue欧芽,從watchedObjects刪除已回收的對象
removeWeaklyReachableObjects()
//生成一個uuid作為key
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
//構(gòu)建當(dāng)前引用的弱引用對象,并關(guān)聯(lián)引用隊列queue
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
//將構(gòu)建的弱引用存入watchedObjects
watchedObjects[key] = reference
checkRetainedExecutor.execute {
//Handler.postDelayed 實現(xiàn)延遲5s執(zhí)行
moveToRetained(key)
}
}
@Synchronized private fun moveToRetained(key: String) {
// 再檢查一遍是否已經(jīng)回收
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
//說明可能存在內(nèi)存泄漏
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
//隊列queue中的對象都是會被GC的
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
//說明釋放了葛圃,從watchedObjects刪除被回收的對象(移除watchedObjects集合中被GC的ref對象千扔,剩下的就可能是泄漏的對象)
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
最后檢查對象沒喲被回收的話,調(diào)用onObjectRetained()方法
onObjectRetained
—>InternalLeakCanary.scheduleRetainedObjectCheck()
—>HeapDumpTrigger.scheduleRetainedObjectCheck
—>HeapDumpTrigger.scheduleRetainedObjectCheck
private fun checkRetainedObjects() {
//...
val config = configProvider()
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
//調(diào)用Runtime.getRuntime().gc()執(zhí)行一次GC装悲,再來看還剩下多少對象未被回收
//GC后Thread.sleep(100)確保對象被GC 等回收的引用入隊
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//當(dāng)前泄漏實例<5昏鹃,不進行heap dump
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
//1分鐘內(nèi)dump過,等會再來
onRetainInstanceListener.onEvent(DumpHappenedRecently)
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
)
scheduleRetainedObjectCheck(
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
dismissRetainedCountNotification()
val visibility = if (applicationVisible) "visible" else "not visible"
//最終調(diào)用dumpHeap
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean,
reason: String
) {
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
//調(diào)用AndroidHeapDumper的dumpHeap()方法—>Debug.dumpHprofData(heapDumpFile.absolutePath)
when (val heapDumpResult = heapDumper.dumpHeap()) {
is HeapDump -> {
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
//清除這次dump之前的引用
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
//通過HeapAnalyzerService 去分析 heap ( 使用Shark庫對heap進行分析)
HeapAnalyzerService.runAnalysis(
context = application,
heapDumpFile = heapDumpResult.file,
heapDumpDurationMillis = heapDumpResult.durationMillis,
heapDumpReason = reason
)
}
}
}
總結(jié)
1.如何初始化
apk打包流程中會把AppWatcherInstaller這個provider合并到app下的mainfest文件中诀诊,ContentProvider的onCreate比Application的onCreate早執(zhí)行,調(diào)用AppWatcher.manualInstall(application)進行初始化的
2.檢測時機
對象 | 如何獲取引用 | 何時納入檢測 |
---|---|---|
Activity | ActivityLifecycleCallbacks回調(diào) | onActivityDestroyed |
Fragment | FragmentLifecycleCallbacks回調(diào) | onFragmentDestroyed |
Fragment中的View | FragmentLifecycleCallbacks回調(diào) | onFragmentViewDestroyed |
ViewModel | 反射獲取ViewModelStore的mMap | ViewModel的onCleared |
RootView | 反射獲取WindowManagerGlobal中的mViews | onViewDetachedFromWindow |
Service | Hook H的mCallback實現(xiàn)阅嘶,當(dāng)消息為STOP_SERVICE時属瓣,從ActivityThread中的mServices獲取 | onServiceDestroyed |
3.檢測原理
當(dāng)jvm進行垃圾回收時,無論內(nèi)存是否充足讯柔,如果該對象只有弱引用存在抡蛙,那么就會被垃圾回收器回收,同時該引用會被加入到關(guān)聯(lián)的ReferenceQueue魂迄。
LeakCanary利用弱引用的特性粗截,獲取當(dāng)前引用,構(gòu)建弱引用對象KeyedWeakReference并關(guān)聯(lián)一個ReferenceQueue捣炬,保存到watchedObjects中熊昌。GC后,通過key刪除已經(jīng)回收的對象湿酸,剩下的對象存在泄漏嫌疑婿屹。