LeakCanary's latest version 2.8.1 source code principle analysis [kotlin version in early 2022]

First, let’s talk about the use of LeakCanary, and then go to the bottom layer to analyze the source code logic

How to use the new version of kotlin

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

Just one step and you're done.

Which leaks are monitored by default

According to the official website, there is no intrusive dependency, and the memory leak monitoring of the following modules will be automatically injected

LeakCanary automatically detects leaks of the following objects:

destroyed Activity instances
destroyed Fragment instances
destroyed fragment View instances
cleared ViewModel instances

overall workflow

It will go through the following 4 steps to do all the work.

  • Detecting retained objects. Monitoring retained objects that have not been recycled
  • Dumping the heap.
  • Analyzing the heap. Analyzing the heap
  • Categorizing leaks. Heap leaks are divided into

Monitor uncollected objects

When it is visible in the foreground, it will start dumping when it detects that there are 5 unrecycled objects

When it is not visible in the background, it will start dumping when it detects that there is an object that has not been recycled.

It is monitored every 2 seconds, and the dump cycle is every 5 seconds.

dump heap

When the above threshold is reached, the dump will be triggered and
the .hprof file will be generated

Analyze Heap

Now it is analyzed by Shark

Leak classification

################################

Step into the topic and smash the source code.

Some providers and activities will be automatically injected into the compiled file.

1: ProcessLifecycleOwnerInitializer

androidx.lifecycle.ProcessLifecycleOwnerInitializer

This is a ContentProvider that comes with Android

In the onCreate method, two operations are mainly done

LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());

1.1: LifecycleDispatcher

//底层会在application中把这个callback纳入application的维护范畴内
((Application) context.getApplicationContext())
                .registerActivityLifecycleCallbacks(new DispatcherActivityCallback());

Paying attention to DispatcherActivityCallback actually did one thing

    @VisibleForTesting
    static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
           //核心就是这句了.
            ReportFragment.injectIfNeededIn(activity);
        }
..........省略.......................
    }

The above callback Callback is an interface in Application, and an ArrayList is maintained in Application

 @UnsupportedAppUsage
    private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
            new ArrayList<ActivityLifecycleCallbacks>();

1.2: ProcessLifecycleOwner

  static void init(Context context) {
        sInstance.attach(context);
    }
void attach(Context context) {
        mHandler = new Handler();
        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
        Application app = (Application) context.getApplicationContext();
        //核心还是下面这行代码了 注册activity的生命周期回调
        app.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
            @Override
            public void onActivityPreCreated(@NonNull Activity activity,
                    @Nullable Bundle savedInstanceState) {
                activity.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
                .......省略.......
                onActivityPostStarted
                .......省略.......
                onActivityPostResumed
               .......省略.......
                });
            }

            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                if (Build.VERSION.SDK_INT < 29) {
                    ReportFragment.get(activity).setProcessListener(mInitializationListener);
                }
            }
          .......省略.......
        });
    }

**Summary: The above two life cycle registration callbacks are ultimately processed in the Application class.

public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
        synchronized (mActivityLifecycleCallbacks) {
            mActivityLifecycleCallbacks.add(callback);
        }
    }

    public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
        synchronized (mActivityLifecycleCallbacks) {
            mActivityLifecycleCallbacks.remove(callback);
        }
    }

2: LeakCanaryFileProvider

leakcanary.internal.LeakCanaryFileProvider

I didn’t understand this class, but it probably means that it is used when operating the file class.

3: MainProcessAppWatcherInstaller

leakcanary.internal.MainProcessAppWatcherInstaller

This class is also integrated with ContentProvider, which replaces the manual install of the old version of LeackCanary. In the onCreate method of this class, the following operations will be automatically performed [Miraculous Pen]

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

3.1: Core code

 @JvmOverloads
  fun manualInstall(
    application: Application,
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
    watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
  ) {
  //校验当前是否在主线程  Looper.getMainLooper().thread === Thread.currentThread()
    checkMainThread()
    if (isInstalled) {
      throw IllegalStateException(
        "AppWatcher already installed, see exception cause for prior install call", installCause
      )
    }
    check(retainedDelayMillis >= 0) {
      "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
    }
    installCause = RuntimeException("manualInstall() first called here")
    this.retainedDelayMillis = retainedDelayMillis
    if (application.isDebuggableBuild) {
    //debug模式 打开日志开关
      LogcatSharkLog.install()
    }
    // Requires AppWatcher.objectWatcher to be set
    LeakCanaryDelegate.loadLeakCanary(application)

    watchersToInstall.forEach {
      it.install()
    }
  }

3.2: Reflective loading of InternalLeakCanary

  @Suppress("UNCHECKED_CAST")
  val loadLeakCanary by lazy {
    try {
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null) as (Application) -> Unit
    } catch (ignored: Throwable) {
      NoLeakCanary
    }
  }

The parameter passed above As Application will be executed to the invoke method

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
..................省略................

override fun invoke(application: Application) {
    _application = application
 //校验是否开启了只在debug模式使用. 设计原理只给debug时候使用
    checkRunningInDebuggableBuild()
    //创建AppWatcher对象 同时设置监听
    AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
    //GC回收工具
    val gcTrigger = GcTrigger.Default

    val configProvider = { LeakCanary.config }
    //创建异步线程
    val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
    handlerThread.start()
    //异步线程用于后台服务
    val backgroundHandler = Handler(handlerThread.looper)

    heapDumpTrigger = HeapDumpTrigger(
      application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,
      configProvider
    )
    //监听应用是否可见的状态 可见和不可见 retained的阈值不一样  5 ---1
    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      //通知更新 如果可能话这里会触发转储堆
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    //监听onResume onPause
    registerResumedActivityListener(application)
    //创建桌面快捷图标 点击直接进入LeakActivity
    addDynamicShortcut(application)
    
 
    mainHandler.post {
     backgroundHandler.post {
        SharkLog.d {
          //校验是否可以dump  如果可以dump的话 则发送notification的广播
          when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) {
            is Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text)
            is Nope -> application.getString(
              R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
            )
          }
        }
      }
    }

To sum up: Through the unique mechanism of ContentProvider in Android, the install operation is automatically triggered, and in the install operation, it is executed through the reflection of the class.

  • Check if only debug mode is enabled
  • Create a Watcher object and listen
  • Create a GC collector
  • Create a background asynchronous thread
  • Monitor whether the application is visible or not, and call different threshold strategies to dump heap
  • Create a desktop shortcut icon

3.3: Automatic injection of different listeners

In the registration of AppWatcher, the last 3 lines have very critical actions, as follows

watchersToInstall.forEach {
      it.install()
    }
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)

The Four King Kong officially debut

  fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
      ActivityWatcher(application, reachabilityWatcher),
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      RootViewWatcher(reachabilityWatcher),
      ServiceWatcher(reachabilityWatcher)
    )
  }

Many friends reported that the old version of LeakCanary has limited functions, only monitors Activity and Fragment, and cannot monitor Service. This time it is arranged.

3.3.1: ActivityWatcher

Inheriting the InstallableWatcher interface has only two methods, install and unInstall, by declaring a global variable to do the following operations

 private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        //watchObject 和 description 这个描述会Log日志
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

  override fun install() {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }

3.3.2: FragmentAndViewModelWatcher

The bottom layer here actually depends on the Activity, and it is also subdivided and compatible with the following
Android8.0 and above

Fragment of Android x series

Android support series of fragments

//定义List<Activity>的集合 
  private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

    //大于等于8.0版本的AndroidOFragmentDestroyWatcher
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
        AndroidOFragmentDestroyWatcher(reachabilityWatcher)
      )
    }

    //AndroidX 里的Fragment
    getWatcherIfAvailable(
      ANDROIDX_FRAGMENT_CLASS_NAME,
      ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      fragmentDestroyWatchers.add(it)
    }

    //support系列里的Fragment
    getWatcherIfAvailable(
      ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
      ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      fragmentDestroyWatchers.add(it)
    }
    fragmentDestroyWatchers
  }

Use of fragmentDestroyWatchers

private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        for (watcher in fragmentDestroyWatchers) {
          watcher(activity)
        }
      }
    }
3.3.2.1: AndroidOFragmentDestroyWatcher
import android.app.Fragment
import android.app.FragmentManager

 private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null) {
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

//底层执行 通过寄存的Activity获取到对应的FragmentManager 设置生命周期回调
  override fun invoke(activity: Activity) {
    val fragmentManager = activity.fragmentManager
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }
3.3.2.2: AndroidXFragmentDestroyWatcher is different from the above

It is written in the same way as AndroidOFragmentDestroyWatcher, the only thing is that the imported fragment package is different, and there are two more rewritten methods for processing

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager

private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    //比androidOFragmentDestroyWatcher多了下面这些处理
    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }

    override fun onFragmentViewDestroyed......省略同AndroidOFragmentDestroyWatcher里..... }
    
     override fun onFragmentDestroyed......省略同AndroidOFragmentDestroyWatcher里..... }


  override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      //比androidOFragmentDestroyWatcher多了下面这一行 
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }

Through the ViewModel in Androidx

  companion object {
    fun install(
      storeOwner: ViewModelStoreOwner,
      reachabilityWatcher: ReachabilityWatcher
    ) {
      val provider = ViewModelProvider(storeOwner, object : Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T =
          ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
      })
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
3.3.2.3: AndroidSupportFragmentDestroyWatcher

Same as AndroidOFragmentDestroyWatcher, the only thing is that the referenced Fragment package is different

import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager

To sum up, when fragmentWatcher is processed, it is handled by distinguishing compatibility, and finally through the fragmentManager in the Activity that the Fragment depends on for life cycle management.

3.3.3: RootViewWatcher

Mainly deal with View related, the premise is that View is not attached to Activity/popWindow, and whether the configuration in the project supports pop-up windows

private val listener = OnRootViewAddedListener { rootView ->
    val trackDetached = when(rootView.windowType) {
      PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
          // Activities are already tracked by ActivityWatcher
            //如果是依附于activity的就不处理
          is Activity -> false
          //如果是弹窗里的 则根据配置文件来觉得
          is Dialog -> {
            // Use app context resources to avoid NotFoundException
            // https://github.com/square/leakcanary/issues/2137
            val resources = rootView.context.applicationContext.resources
            resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
          }
          // Probably a DreamService
          else -> true
        }
      }
      // Android widgets keep detached popup window instances around.
      //依赖于pop window的也不处理
      POPUP_WINDOW -> false
      TOOLTIP, TOAST, UNKNOWN -> true
    }
    //可溯源追踪的就执行如下
    if (trackDetached) {
      rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
      
        val watchDetachedView = Runnable {
          reachabilityWatcher.expectWeaklyReachable(
            rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
          )
        }

        override fun onViewAttachedToWindow(v: View) {
          mainHandler.removeCallbacks(watchDetachedView)
        }

        override fun onViewDetachedFromWindow(v: View) {
          mainHandler.post(watchDetachedView)
        }
      })
    }
  }

3.3.4: ServiceWatcher

weak reference association

private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()

reflection creation

 private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }

  private val activityThreadInstance by lazy {
    activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
  }

  private val activityThreadServices by lazy {
    val mServicesField =
      activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }

    @Suppress("UNCHECKED_CAST")
    mServicesField[activityThreadInstance] as Map<IBinder, Service>
  }

The core source code of the service has not been understood for the time being, and it is probably known that there is reflection calling IActivityManager

Paste the core source code in the install method

try {
      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback
          }
        }
        Handler.Callback { msg ->
          // https://github.com/square/leakcanary/issues/2114
          // On some Motorola devices (Moto E5 and G6), the msg.obj returns an ActivityClientRecord
          // instead of an IBinder. This crashes on a ClassCastException. Adding a type check
          // here to prevent the crash.
          if (msg.obj !is IBinder) {
            return@Callback false
          }

          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            activityThreadServices[key]?.let {
              onServicePreDestroy(key, it)
            }
          }
          mCallback?.handleMessage(msg) ?: false
        }
      }
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        uninstallActivityManager = {
          swapActivityManager { _, _ ->
            activityManagerInstance
          }
        }
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              onServiceDestroyed(token)
            }
          }
          try {
            if (args == null) {
              method.invoke(activityManagerInstance)
            } else {
              method.invoke(activityManagerInstance, *args)
            }
          } catch (invocationException: InvocationTargetException) {
            throw invocationException.targetException
          }
        }
      }
    } catch (ignored: Throwable) {
      SharkLog.d(ignored) { "Could not watch destroyed services" }
    }

In summary, we know why the new version of leakCanary only needs to depend on it. Because the above is automatically processed.

4: Then analyze the dump heap area

We maintain an ObjectWatcher class in the AppWatcher class

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 {

4.1: In the AppWatcher class as follows

  @Volatile
  var retainedDelayMillis = RETAINED_DELAY_NOT_SET
  
  
 val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
      //重要操作 发送延迟操作的任务  
      mainHandler.postDelayed(it, retainedDelayMillis)
    },//传
    isEnabled = { true }
  ),
    isEnabled = { true }

The handler mentioned above is the handler of the main thread

internal val mainHandler by lazy { Handler(Looper.getMainLooper()) }

In the invoke execution method of InternalLeakCanary, there is an operation to send Notification when monitoring

5: NotificationReceiver

This class is mainly responsible for the operation of receiving the broadcast event DUMP_HEAP,

5.1: In the above step 3.3, you can see the processing of sending broadcast

backgroundHandler.post {
        SharkLog.d {
          //校验是否可以dump  如果可以dump的话 则发送notification的广播
          when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) {
            is Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text)
            is Nope -> application.getString(
              R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
            )
          }
        }
      }

5.2 In the HenapDumpControl class

fun iCanHasHeap(): ICanHazHeap {
    ........省略代码.........

    synchronized(this) {
      if (::latest.isInitialized && dumpHeap is Yup && latest is Nope) {
        //dump的调度处理
        InternalLeakCanary.scheduleRetainedObjectCheck()
      }
      latest = dumpHeap
    }

    return dumpHeap
  }

5.3: In INternalLeakCanary

  fun scheduleRetainedObjectCheck() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.scheduleRetainedObjectCheck()
    }
  }

5.4 In HeapDumpTrigger

fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      return
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      //检查是否需要dump的地方 根据需要的时候就发送广播出去
      checkRetainedObjects()
    }, delayMillis)
  }

5.6 Receive processing in broadcast

 override fun onReceive(
    context: Context,
    intent: Intent
  ) {
    when (intent.action) {
      DUMP_HEAP.name -> {
      //具体的执行看下面5.7
        InternalLeakCanary.onDumpHeapReceived(forceDump = false)
      }
      CANCEL_NOTIFICATION.name -> {
        // Do nothing, the notification has auto cancel true.
      }
      else -> {
        SharkLog.d { "NotificationReceiver received unknown intent action for $intent" }
      }
    }
  }

5.7: Dump reception processing in the InternalLeakCanary class

 fun onDumpHeapReceived(forceDump: Boolean) {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.onDumpHeapReceived(forceDump)
    }
  }

##5.8 Processing in HeapDumpTrigger

fun onDumpHeapReceived(forceDump: Boolean) {
    backgroundHandler.post {
      //取消notify提示
      dismissNoRetainedOnTapNotification()
      //手动执行GC 底层调用 Runtime.getRuntime().gc()
      gcTrigger.runGc()
      val retainedReferenceCount = objectWatcher.retainedObjectCount
      if (!forceDump && retainedReferenceCount == 0) {
       ....省略代码.......
        return@post
      }

      SharkLog.d { "Dumping the heap because user requested it" }
      //重要操作
      dumpHeap(retainedReferenceCount, retry = false, "user request")
    }
  }

5.9 dumpHeap processing

 private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean,
    reason: String
  ) {
    //创建存储dump 文件的 目录
    val directoryProvider =
      InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
    //在dump文件夹创建新的dump文件    目录context.cacheDir
    val heapDumpFile = directoryProvider.newHeapDumpFile()

    val durationMillis: Long
    try {
      //发送事件  当前的事件唯一id
      InternalLeakCanary.sendEvent(DumpingHeap(currentEventUniqueId))
      if (heapDumpFile == null) {
        throw RuntimeException("Could not create heap dump file")
      }
      saveResourceIdNamesToMemory()
      val heapDumpUptimeMillis = SystemClock.uptimeMillis()
      //UUID为key 的一个弱引用因 
      KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
      durationMillis = measureDurationMillis {
        configProvider().heapDumper.dumpHeap(heapDumpFile)
      }
      if (heapDumpFile.length() == 0L) {
        throw RuntimeException("Dumped heap file is 0 byte length")
      }
      lastDisplayedRetainedObjectCount = 0
      lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
      objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
      currentEventUniqueId = UUID.randomUUID().toString()
      InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId, heapDumpFile, durationMillis, reason))

5.10 Event collection in LeakCanary class

 val eventListeners: List<EventListener> = listOf(
      LogcatEventListener,
      ToastEventListener,
      LazyForwardingEventListener {
        if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
      },
      when {
          RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
            RemoteWorkManagerHeapAnalyzer
          WorkManagerHeapAnalyzer.workManagerInClasspath -> WorkManagerHeapAnalyzer
          else -> BackgroundThreadHeapAnalyzer
      }
    ),

5.10.1: RemoteWorkManagerHeapAnalyzer

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      val application = InternalLeakCanary.application
      val heapAnalysisRequest =
        OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class.java).apply {
          val dataBuilder = Data.Builder()
            .putString(ARGUMENT_PACKAGE_NAME, application.packageName)
            .putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)
          setInputData(event.asWorkerInputData(dataBuilder))
          with(WorkManagerHeapAnalyzer) {
            addExpeditedFlag()
          }
        }.build()
      SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
      val workManager = WorkManager.getInstance(application)
      //入栈 执行workQueue
      workManager.enqueue(heapAnalysisRequest)
    }

Finally, several data classes HeapAnalysisFailure HeapAnalysisSuccess in HeapAnalysis are called, and the toString() method inside is the spliced ​​error record we saw in LeakCanary.

Summary section code

data class HeapAnalysisFailure(
  override val heapDumpFile: File,
  override val createdAtTimeMillis: Long,
  override val dumpDurationMillis: Long = DUMP_DURATION_UNKNOWN,
  override val analysisDurationMillis: Long,
  /**
   * An exception wrapping the actual exception that was thrown.
   */
  val exception: HeapAnalysisException
) : HeapAnalysis() {

  override fun toString(): String {
    return """====================================
HEAP ANALYSIS FAILED

You can report this failure at https://github.com/square/leakcanary/issues
Please provide the stacktrace, metadata and the heap dump file.
====================================
STACKTRACE

$exception====================================
METADATA

Build.VERSION.SDK_INT: ${androidSdkInt()}
Build.MANUFACTURER: ${androidManufacturer()}
LeakCanary version: ${leakCanaryVersion()}
Analysis duration: $analysisDurationMillis ms
Heap dump file path: ${heapDumpFile.absolutePath}
Heap dump timestamp: $createdAtTimeMillis
===================================="""
  }

5.10.2: WorkManagerHeapAnalyzer

Similar to 5.10.1 with fewer steps

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
        setInputData(event.asWorkerInputData())
        addExpeditedFlag()
      }.build()
      SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
      val application = InternalLeakCanary.application
      WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
    }
  }

5.10.3: BackgroundThreadHeapAnalyzer

override fun onEvent(event: Event) {
    if (event is HeapDump) {
      heapAnalyzerThreadHandler.post {
        val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
          InternalLeakCanary.sendEvent(event)
        }
        InternalLeakCanary.sendEvent(doneEvent)
      }
    }
  }

Above: LeakCanary's dependency monitoring dump and analysis are basically clarified.

6 PlumberInstaller

leakcanary.internal.PlumberInstaller

In addition, add food to supplement this. The function of this class is mainly to reflect whether different versions and different mobile phone manufacturers support some APIs.

Mainly for different models and different versions, some special scenarios will cause Leak, manually set to null when the Activity is destroyed to facilitate recycling.

AndroidLeakFixes.applyFixes(application)

This analysis is performed in the background by a single thread pool

 Executors.newSingleThreadScheduledExecutor

For example, for example, there is a version for Samsung devices that is not between 19 and 21
Reflecting the mLastHoveredView field in TextView

  override fun apply(application: Application) {
      if (MANUFACTURER != SAMSUNG || SDK_INT !in 19..21) {
        return
      }

      backgroundExecutor.execute {
        val field: Field
        try {
          field = TextView::class.java.getDeclaredField("mLastHoveredView")
          field.isAccessible = true
        } catch (ignored: Exception) {
          SharkLog.d(ignored) { "Could not fix the $name leak" }
          return@execute
        }

        application.onActivityDestroyed {
          try {
            field.set(null, null)
          } catch (ignored: Exception) {
            SharkLog.d(ignored) { "Could not fix the $name leak" }
          }
        }
      }
    }
  },

LeakActivity

leakcanary.internal.activity.LeakActivity

We open the canary icon to show this Activity.

Import the .hprof file

 private fun importHprof(fileUri: Uri) {
    try {
      contentResolver.openFileDescriptor(fileUri, "r")
        ?.fileDescriptor?.let { fileDescriptor ->
          val inputStream = FileInputStream(fileDescriptor)
          InternalLeakCanary.createLeakDirectoryProvider(this)
            .newHeapDumpFile()
            ?.let { target ->
              inputStream.use { input ->
                target.outputStream()
                  .use { output ->
                    input.copyTo(output, DEFAULT_BUFFER_SIZE)
                  }
              }
              InternalLeakCanary.sendEvent(
                HeapDump(
                  uniqueId = UUID.randomUUID().toString(),
                  file = target,
                  durationMillis = -1,
                  reason = "Imported by user"
                )
              )
            }
        }
    } catch (e: IOException) {
      SharkLog.d(e) { "Could not imported Hprof file" }
    }
  }

RequestStoragePermissionActivity

It is mainly used to apply for permission.

  • From the perspective of design mode, the factory mode (Wacher and analyzer) is used
  • Cleverly use the ContentProvider feature to achieve non-intrusive one-line code access
  • Compared with the old version, the monitoring of RootView and Service has been added

Note: This official announcement can only be made in debug mode.

The above is the overall analysis.

Guess you like

Origin blog.csdn.net/admin_jalen/article/details/122640736