LeakCanary源码解析

1,299 阅读3分钟

一:前言

最近看了看LeakCanary的源码,这里记录一下以防忘记,如有不足,恳请指出。

二:使用

LeakCanary的2.0版本使用特别简单,只需在项目build.gradle添加以下一句代码即可使用

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
}

三:解析

1.初始化

很多人看了之后就有疑惑了,怎么不用初始化呢,这怎么可能就可以使用了。尤其是使用过LeakCanary1.0版本的看官肯定满肚子疑惑,请听我慢慢道来。我们打开源码,找到AppWatcherInstaller,打开源码如下

/**
 * Content providers are loaded before the application class is created. [AppWatcherInstaller] is
 * used to install [leakcanary.AppWatcher] on application start. 初始化类,在Manifest中注册了
 */
internal sealed class AppWatcherInstaller : ContentProvider() {

  /**
   * [MainProcess] automatically sets up the LeakCanary code that runs in the main app process.
   */
  internal class MainProcess : AppWatcherInstaller()

  /**
   * When using the `leakcanary-android-process` artifact instead of `leakcanary-android`,
   * [LeakCanaryProcess] automatically sets up the LeakCanary code
   */
  internal class LeakCanaryProcess : AppWatcherInstaller()

  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)//真正初始化之处
    return true
  }

---省略
}

我们都知道ContentProvider是四大组件之一,必须注册。那她为什么能初始化金丝雀(即LeakCanary),它能保证Application不为空吗? 原理: ContentProvider的onCreate的调用时机介于Application的attachBaseContext和onCreate之间,Provider的onCreate优先于Application的onCreate执行,并且此时的Application已经创建成功,而Provider里的context正是Application的对象

注意点: 使用ContentProvider初始化的方式,我们需要注意一下几点: 首先,在Manifest里设置ContentProvider的时候要设置一个authorities,这个authorities相当于ContentProvider的标识,是不能重复的,为了保证不重复,我们再设置这个值的时候最好不要硬编码,而是使用以下的这种方式:使用appid+xxx

<provider
    android:authorities="${applicationId}.xxprovider"
    android:name=".MyLibRuntimeProvider"
    android:exported="false"/>

由ActivityThread的handleBindApplication方法可以看到,是先调用installContentProviders方法,然后调用mInstrumentation.callApplicationOnCreate方法的。

2.原理

我们现在知道为什么不需要在Application初始化了,现在我们来理理金丝雀的工作原理。我们通过这个初始化的代码点进去AppWatcher

@JvmOverloads
  fun manualInstall(
    application: Application,
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
    watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
  ) {
    checkMainThread()
    check(!isInstalled) {
      "AppWatcher already installed"
    }
    check(retainedDelayMillis >= 0) {
      "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
    }
    this.retainedDelayMillis = retainedDelayMillis
    if (application.isDebuggableBuild) {
      LogcatSharkLog.install()
    }
    // Requires AppWatcher.objectWatcher to be set
    LeakCanaryDelegate.loadLeakCanary(application)

    watchersToInstall.forEach {
      it.install()
    }
  }
  
  /**
   * Creates a new list of default app [InstallableWatcher], created with the passed in
   * [reachabilityWatcher] (which defaults to [objectWatcher]). Once installed,
   * these watchers will pass in to [reachabilityWatcher] objects that they expect to become
   * weakly reachable.
   *
   * The passed in [reachabilityWatcher] should probably delegate to [objectWatcher] but can
   * be used to filter out specific instances.初始化各种监听器比如activity、fragment等
   */
  fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
      ActivityWatcher(application, reachabilityWatcher),
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      RootViewWatcher(reachabilityWatcher),
      ServiceWatcher(reachabilityWatcher)
    )
  }
  
   /**
   * The [ObjectWatcher] used by AppWatcher to detect retained objects.
   * Only set when [isInstalled] is true.
   */
  val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
      mainHandler.postDelayed(it, retainedDelayMillis)//特定时间轮询一次
    },
    isEnabled = { true }
  )

通过代码我们可以看到,里面都有一个objectWatcher,那这个objectWatcher是何方神圣呢。其实它是所有监听器的处理基类,其他的监听器都是通过监听页面生命周期来调用objectWatcher最终处理,比如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)
  }
}

那么我们再来看看ObjectWatcher是怎么处理的,主要代码如下:

class ObjectWatcher constructor(
  private val clock: Clock,
  private val checkRetainedExecutor: Executor,
  /**
   * Calls to [watch] will be ignored when [isEnabled] returns false
   */
  private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {

  private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>() 

  /**
   * References passed to [watch].
   */
  private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()//通过map集合来实现当前有几个泄露对象
  private val queue = ReferenceQueue<Any>() //主要实现原理,通过弱引用对象队列查找
   @Synchronized override fun expectWeaklyReachable(
    watchedObject: Any,
    description: String
  ) {
    if (!isEnabled()) {
      return
    }
    removeWeaklyReachableObjects()
    val key = UUID.randomUUID()
      .toString()
    val watchUptimeMillis = clock.uptimeMillis()
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)//创建弱引用对象
    SharkLog.d {
      "Watching " +
        (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
        (if (description.isNotEmpty()) " ($description)" else "") +
        " with key $key"
    }

    watchedObjects[key] = reference//添加key value值
    checkRetainedExecutor.execute {//通过mainHandler.postDelayed()实现轮询查找队列里面的对象是否是弱引用,是则去除,否则内存泄露,通知用户
      moveToRetained(key)
    }
  }
  @Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }

  private fun removeWeaklyReachableObjects() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?//查找是否是弱引用之处
      if (ref != null) {
        watchedObjects.remove(ref.key)
      }
    } while (ref != null)
  }

总结

一顿分析猛如虎,一看工资两百五。最后总结一下原理:

  • 首先通过ContentProvider 初始化金丝雀
  • 然后初始化各种监听器
  • 通过监听页面销毁或者主动把对象加入监听,把对象加入到弱引用队列里面
  • 通过定时轮询和queue.poll()方法查找对象是否是弱引用,如果过了某个时间,对象还是没有变为弱引用,则报泄露,否则移除队列

订单.png 还有分析内存泄漏原因等原理后面看了再写