Android source code analysis: Leakcanary is a good helper for memory leak detection

Let’s analyze the famous Leakcanary together. You must have been exposed to it as an Android developer to some extent. The new version of Leakcanary has also been rewritten in Kotlin. I recently checked the source code in detail and shared it.

Tips: Originally I just wanted to analyze the memory leak detection part, but I got sidetracked as I wrote it, because the difficulty in detecting memory leaks lies in controlling the object life cycle. I think Leakcanary is very worthwhile for controlling the Service life cycle . We learn it and use it in our projects. In addition, Leakcanary is rewritten in Kotlin. I have never used some syntactic sugar, so I just wrote it down. The whole thing is a bit verbose to read.

Source code version

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

memory leak

This blog focuses on the analysis of leakcanary source code implementation principles and some excellent designs. The explanation of memory leaks is simply explained with my own understanding: long-life cycle objects hold short-life cycle objects. When short-life cycle objects need to be recycled, It is held by a long-life cycle object and cannot be recycled normally;

Source code analysis

Before understanding its excellent design, let’s briefly analyze its source code and implementation principles.

If you want the full version of the study materials, please click to get it for free

Detection principle

The principle of Leakcanary to detect memory leaks is very simple, it is to use the two-parameter construction method of WeakReference.

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

To detect whether the weakly referenced object is recycled and released normally, for example:

// 定义类
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 {
   
    
    
    // 未回收
}

Activities, Fragments, Services, and custom Views in Android development are all objects prone to memory leaks. What Leakcanary does is to weaken these objects at the appropriate time (usually during recycling, such as after Activity's onDestory). Reference and associate the reference queue, and determine whether a leak occurs based on whether it is added to the reference queue.

The above sample code has briefly demonstrated the principle of determining whether an object is leaked. Let's follow the source code to take a look at the implementation details of Leakcanary.

initialization

Leakcanary only needs to introduce dependencies to complete the initialization. This is not a magical trick now. It uses ContentProvider.

The initialization time of ContentProvider is before the onCreate of Application, and the context and applicationContext can be obtained in the onCreate method of ContentProvider.

When the project introduces Leakcanary, the registration can be found in the manifest file of the packaged apk MainProcessAppWatcherInstaller. The key source code part is as follows:

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

It can be seen that is called for AppWatcher.manualInstall()initialization, and its source code is as follows:

AppWatcher.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(application)

The third parameter in the above initialization method watchersToInstallis given a default value, and appDefaultWatchersone is obtained through List<InstallableWatcher>. Let’s first look at the InstallableWatcher source code:

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

It is an interface that defines two methods. You can understand installation and uninstallation by looking at the names. Next, let’s take a look at what the appDefaultWatchers method returns:

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 内存泄漏
  )
}

Note that the second parameter of the above method reachabilityWatcheris assigned a value by default objectWatcher:

val objectWatcher = ObjectWatcher(...)

First remember that it is an instance of the ObjectWatcher class, and it is passed to each InstallableWatcher implementation class for detection.

LeakCanaryDelegate.loadLeakCanary(application)

Then go back and look at LeakCanaryDelegate.loadLeakCanary(application)this code. loadLeakCanary is a function type variable in the LeakCanaryDelegate class, so it can be called directly. Take a look at its source code:

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() {
   
    
    }
  }
}

Under normal circumstances, LeakCanaryDelegate.loadLeakCanary(application) is equivalent to calling InternalLeakCanary. Of course, InternalLeakCanary is also an implementation of (Application) -> Unit. You read that right. Function types can not only declare variables, but also define implementation classes, but they need to To implement invokethe method, take a look at its source code:

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

Guess you like

Origin blog.csdn.net/m0_70748458/article/details/130485368