LeakCanary

在这里插入图片描述

自动安装

  1. 直接添加debug依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'
  1. 自动安装的原理ContentProvider
/**
 * Content providers are loaded before the application class is created. [AppWatcherInstaller] is
 * used to install [leakcanary.AppWatcher] on application start.
 */
internal sealed class AppWatcherInstaller : ContentProvider() {
 override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    InternalAppWatcher.install(application)
    return true
  }
}

AMS的启动流程 :Application->attachBaseContext =====>ContentProvider->onCreate =====>Application->onCreate ContentProvider

  1. 安装的优化
    默认是在APP的主进程的主线程安装,虽然只在Debug版本,仍然可能比较卡,2种方法可以优化,注意,不论怎样,都必须是在主线程初始化checkMainThread()

    . 去掉自动安装,在另外一个进程初始化安装

<?xml version="1.0" encoding="utf-8"?>
 <resources>
      <bool name="leak_canary_watcher_auto_install">false</bool>
 </resources>
/**
 * The entry point API for using [ObjectWatcher] in an Android app. [AppWatcher.objectWatcher] is
 * in charge of detecting retained objects, and [AppWatcher] is auto configured on app start to
 * pass it activity and fragment instances. Call [ObjectWatcher.watch] on [objectWatcher] to
 * watch any other object that you expect to be unreachable.
 */
object AppWatcher {
/**
   * [AppWatcher] is automatically installed on main process start by
   * [leakcanary.internal.AppWatcherInstaller] which is registered in the AndroidManifest.xml of
   * your app. If you disabled [leakcanary.internal.AppWatcherInstaller] or you need AppWatcher
   * or LeakCanary to run outside of the main process then you can call this method to install
   * [AppWatcher].
   */
  fun manualInstall(application: Application) = InternalAppWatcher.install(application)
}
google的StartUp框架

安装过程

核心逻辑是注册生命回调application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks) ,逻辑封装在ObjectWatcher

internal object InternalAppWatcher {
  fun install(application: Application) {
    SharkLog.logger = DefaultCanaryLog()
    SharkLog.d { "Installing AppWatcher" }
    checkMainThread()
    if (this::application.isInitialized) {
      return
    }
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
  }

以Activity为例

internal class ActivityDestroyWatcher private constructor(
  private val objectWatcher: ObjectWatcher,
  private val configProvider: () -> Config
) {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
          objectWatcher.watch(activity)
        }
      }
    }

  companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
  }
}

监测逻辑

  1. Activity的onDestroy()--->ObjectWatcher.watch()
/**
 * [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances
 * that reference watches objects, and check if those references have been cleared as expected on
 * the [checkRetainedExecutor] executor. If not, these objects are considered retained and
 * [ObjectWatcher] will then notify the [onObjectRetainedListener] on that executor thread.
 *
 * [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant
 * to give the GC the opportunity to identify weakly reachable objects.
 *
 * [ObjectWatcher] is thread safe.
 */
class ObjectWatcher constructor(
  @Synchronized fun watch(
    watchedObject: Any,
    name: String
  ) {
    //1、检查清除,利用引用队列的逻辑
    removeWeaklyReachableObjects()
    val key = UUID.randomUUID().toString()
    val watchUptimeMillis = clock.uptimeMillis()
    //2、弱引用,注册引用队列queue
    val reference =
      KeyedWeakReference(watchedObject, key, name, watchUptimeMillis, queue)
  
    //3、mutableMapOf<String, KeyedWeakReference>()临时引用保存在map
    watchedObjects[key] = reference
    //4、延迟检测默认5秒之后再检查是否weakly  reachable
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }
  //会反复调用检测
 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)
  }
  
  //线程池,Default delay to 5 seconds
  private val checkRetainedExecutor = Executor {
    mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis)
  }
  // How long to wait before reporting a watched object as retained.
  val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5)
  
  
  1. 后台线程处理moveToRetained
 @Synchronized private fun moveToRetained(key: String) {
    //反复检查
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
     //5.仍然没有weakly reachable,交给子线程处理
    if (retainedRef != null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
 }
 
 //最终的回调逻辑,封装在了HeapDumpTrigger类
 internal class HeapDumpTrigger{
  fun onObjectRetained() {
    scheduleRetainedObjectCheck("found new object retained")
  }

  private fun scheduleRetainedObjectCheck(reason: String) {
    if (checkScheduled) {
      SharkLog.d { "Already scheduled retained check, ignoring ($reason)" }
      return
    }
    checkScheduled = true
    backgroundHandler.post {
      checkScheduled = false
      //5、后台检测线程开始工作
      checkRetainedObjects(reason)
    }
  }

 //伪代码,保留了核心逻辑
 private fun checkRetainedObjects(reason: String) {
    SharkLog.d { "Checking retained object because $reason" }
    //获取retainedObjectCount的时候,会再调用removeWeaklyReachableObjects
    var retainedReferenceCount = objectWatcher.retainedObjectCount
    //1.手动gc一次
    if (retainedReferenceCount > 0) {
      gcTrigger.runGc()
      //再调用removeWeaklyReachableObjects,更新数量
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }

    //2.如果数量小于5,而且APP可见,只显示APP通知,然后delay2秒,继续轮训检测
     if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
     
    SharkLog.d { "Found $retainedReferenceCount retained references, dumping the heap" }
    //3.Dumping the heap freezes the UI ,不能轻易的dump
    //如果数量大于5或者APP不可见,开始Dumps the heap into a file
    val heapDumpFile = heapDumper.dumpHeap()
    
    //4开启单独的服务,解析hprof文件
    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
 }
 
 private fun checkRetainedCount(
    retainedKeysCount: Int,
    retainedVisibleThreshold: Int
  ): Boolean {
  if (retainedKeysCount < retainedVisibleThreshold) {
      if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
      //通知栏显示有泄露的信息
        showRetainedCountBelowThresholdNotification(retainedKeysCount, retainedVisibleThreshold)
        //delay2秒继续轮训检测
        scheduleRetainedObjectCheck(
            "Showing retained objects notification", WAIT_FOR_OBJECT_THRESHOLD_MILLIS
        )
        return true
      }
    }
    return false
 }
 }
  /**
     * When the app is visible, LeakCanary will wait for at least
     * [retainedVisibleThreshold] retained instances before dumping the heap. Dumping the heap
     * freezes the UI and can be frustrating for developers who are trying to work. This is
     * especially frustrating as the Android Framework has a number of leaks that cannot easily
     * be fixed.
     *
     * When the app becomes invisible, LeakCanary dumps the heap after
     * [AppWatcher.Config.watchDurationMillis] ms.
     *
     * The app is considered visible if it has at least one activity in started state.
     *
     * A higher threshold means LeakCanary will dump the heap less often, therefore it won't be
     * bothering developers as much but it could miss some leaks.
     *
     * Defaults to 5.
     */
    val retainedVisibleThreshold: Int = 5
  1. 解析hprof文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值