Android 소스 코드 분석: Leakcanary는 메모리 누수 감지에 좋은 도우미입니다.

그 유명한 Leakcanary를 함께 분석해보겠습니다. 안드로이드 개발자라면 어느 정도 접해봤을 텐데요. 새 버전의 Leakcanary도 Kotlin으로 재작성되었습니다. 최근에 소스코드를 자세히 확인해서 공유해 드렸습니다.

Tips: 원래는 메모리 누수 감지 부분만 분석하려고 했으나, 메모리 누수 감지의 어려움이 객체 생명주기를 제어하는 ​​데 있기 때문에 작성하다보니 곁길로 빠졌습니다. 서비스 생명주기를 제어하는데는 Leakcanary가 매우 가치가 있다고 생각합니다. .우리는 그것을 배우고 프로젝트에 사용합니다. 게다가 Leakcanary는 Kotlin으로 다시 작성되었습니다. 문법적 설탕을 사용한 적이 없어서 그냥 적어 두었습니다. 전체 내용이 읽기에는 약간 장황합니다.

소스코드 버전

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

메모리 누수

이 블로그에서는 Leakcanary 소스 코드 구현 원리와 몇 가지 우수한 디자인에 대한 분석에 중점을 두고 있습니다. 메모리 누수에 대한 설명은 내 자신의 이해를 바탕으로 간단하게 설명됩니다. 수명 주기가 긴 객체는 수명 주기가 짧은 객체를 보유합니다. 수명 주기가 짧은 객체가 필요할 때 재활용될 수 있으며, 수명이 긴 물체에 의해 보유되며 정상적으로 재활용될 수 없습니다.

소스코드 분석

뛰어난 디자인을 이해하기 전에 소스코드와 구현 원리를 간략하게 분석해 보겠습니다.

학습 자료의 정식 버전을 원하시면 클릭하여 무료로 받으세요

감지 원리

메모리 누수를 감지하는 Leakcanary의 원리는 매우 간단합니다. WeakReference의 2개 매개변수 구성 방법을 사용하는 것입니다.

WeakReference(T referent, ReferenceQueue<? super T> q)

약하게 참조된 객체가 정상적으로 재활용되고 해제되는지 여부를 감지하려면 다음을 수행하십시오.

// 定义类
class A
// 检测的目标对象
val obj = A()
val queue = ReferenceQueue<A>()
val weakObj = WeakReference(obj, queue)
// 触发gc回收(注意:这样的操作不一定可以触发gc,具体如何触发gc 在下面的源码分析中有提到 leakcanary 是如何触发gc的)
System.gc()

val tmp = queue.poll()
if (tmp === obj) {
   
    
    
    // 被回收
} else {
   
    
    
    // 未回收
}

Android 개발의 Activity, Fragments, Services 및 Custom Views는 모두 메모리 누수가 발생하기 쉬운 객체입니다. Leakcanary가 하는 일은 적절한 시간(보통 Activity의 onDestory 이후와 같이 재활용하는 동안)에 이러한 객체를 약화시키는 것입니다. 참조 큐를 참조하고 연결합니다. , 참조 큐에 추가되었는지 여부에 따라 누수가 발생하는지 여부를 판단합니다.

위의 샘플 코드에서는 객체의 유출 여부를 판별하는 원리를 간략하게 설명했는데, 소스 코드를 따라가면서 Leakcanary의 구현 내용을 살펴보겠습니다.

초기화

Leakcanary는 초기화를 완료하기 위해 종속성을 도입하기만 하면 됩니다. 이는 현재 마술이 아니라 ContentProvider를 사용합니다.

ContentProvider의 초기화 시점은 Application의 onCreate 이전이며, ContentProvider의 onCreate 메소드에서 context와 applicationContext를 얻을 수 있다.

프로젝트에 Leakcanary가 도입되면 패키징된 apk의 매니페스트 파일에서 등록 내용을 확인할 수 있으며 MainProcessAppWatcherInstaller, 주요 소스 코드 부분은 다음과 같습니다.

internal class MainProcessAppWatcherInstaller : ContentProvider() {
   
    
    
  override fun onCreate(): Boolean {
   
    
    
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }
  //..
}

초기화를 위해 호출되는 것을 볼 수 있으며 AppWatcher.manualInstall(), 소스코드는 다음과 같습니다.

앱와처.kt

fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默认 5s
  watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) // 获取默认Watchers 下面详细分析
) {
   
    
    
  // 检查是否在主线程
  // 原理:Looper.getMainLooper().thread === Thread.currentThread()
  checkMainThread()
  // ...
  this.retainedDelayMillis = retainedDelayMillis
  // 初始化 Shark 库
  if (application.isDebuggableBuild) {
   
    
    
    LogcatSharkLog.install()
  }
  // 这行代码下面详细分析
  LeakCanaryDelegate.loadLeakCanary(application)
  // 对 watchers 遍历调用 install 
  watchersToInstall.forEach {
   
    
    
    it.install()
  }
  // 给 installCause 赋值,代表已经初始化
  installCause = RuntimeException("manualInstall() first called here")
}

appDefaultWatchers(응용 프로그램)

위 초기화 메소드의 세 번째 매개변수에는 watchersToInstall기본값이 주어지며, appDefaultWatchers하나는 를 통해 얻습니다 List<InstallableWatcher>. 먼저 InstallableWatcher 소스 코드를 살펴보겠습니다.

interface InstallableWatcher {
   
    
    
  fun install()
  fun uninstall()
}

두 가지 메소드를 정의하는 인터페이스입니다. 이름을 보면 설치 및 제거를 이해할 수 있습니다. 다음으로 appDefaultWatchers 메소드가 반환하는 내용을 살펴보겠습니다.

fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher // 注意这个 objectWatcher 很重要
): List<InstallableWatcher> {
   
    
    
  return listOf(
    ActivityWatcher(application, reachabilityWatcher), // 用于监控activity内存泄漏
    FragmentAndViewModelWatcher(application, reachabilityWatcher),// 用于监控fragment,viewmodel 内存泄漏
    RootViewWatcher(reachabilityWatcher),// 用于监听 rootview 内存泄漏
    ServiceWatcher(reachabilityWatcher) // 用于监听 service 内存泄漏
  )
}

위 메소드의 두 번째 매개변수에는 reachabilityWatcher기본적으로 값이 할당됩니다 objectWatcher.

val objectWatcher = ObjectWatcher(...)

먼저 이는 ObjectWatcher 클래스의 인스턴스이며 감지를 위해 각 InstallableWatcher 구현 클래스에 전달된다는 점을 기억하세요.

LeakCanaryDelegate.loadLeakCanary(응용 프로그램)

그런 다음 돌아가서 LeakCanaryDelegate.loadLeakCanary(application)이 코드를 살펴보세요. loadLeakCanary는 LeakCanaryDelegate 클래스의 함수 유형 변수이므로 직접 호출할 수 있습니다. 소스 코드를 살펴보세요.

internal object LeakCanaryDelegate {
   
    
    
  // 延迟初始化
  val loadLeakCanary by lazy {
   
    
    
    try {
   
    
    
      // 默认加载 InternalLeakCanary 获取其 INSTANCE 字段
      // InternalLeakCanary 是一个 object class,编译为 java 后会自动生成 INSTANCE
      // 就是一个单例类 这里是获取其单例对象
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null) as (Application) -> Unit
    } catch (ignored: Throwable) {
   
    
    
      // 出现异常时返回 NoLeakCanary
      NoLeakCanary
    }
  }
  // (Application) -> Unit 函数类型变量,接受一个 application 作为参数
  // 内部都是空实现
  object NoLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
   
    
    
    override fun invoke(application: Application) {
   
    
    }
    override fun onObjectRetained() {
   
    
    }
  }
}

일반적인 상황에서 LeakCanaryDelegate.loadLeakCanary(application)은 InternalLeakCanary를 호출하는 것과 같습니다. 물론 InternalLeakCanary도 (Application) -> Unit의 구현입니다. 그렇군요. 읽어보시면 알겠지만, 함수 유형은 변수 선언뿐만 아니라 구현 클래스도 정의할 수 있습니다. 하지만 메서드를 구현하려면 invoke해당 소스 코드를 살펴보세요.

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
   
    
    
    override fun invoke(application

추천

출처blog.csdn.net/m0_70748458/article/details/130485368