A: the preface

Recently I have looked at the source code of LeakCanary. Please note it here in case you forget it. If there is any deficiency, please point it out.

2: use

Version 2.0 of LeakCanary is particularly easy to use by adding the following code to the build.gradle project

dependencies { // debugImplementation because LeakCanary should only run in debug builds. debugImplementation 'com. Squareup. Leakcanary: leakcanary - android: 2.6'}Copy the code

Three: parsing

1. Initialization

A lot of people look at it and wonder, how can I not initialize it, how can I possibly use it? In particular, anyone who has used Leakary version 1.0 must be wondering, so let me explain. We open the source, find the AppWatcherInstaller, open the source code as follows

/** * 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() { /** * [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} -- omit}Copy the code

We all know that A ContentProvider is one of the four components that must be registered. So why can she initialize canary (i.e., LeakCanary), and does it guarantee that Application is not empty? Principle: ContentProvider onCreate is called between Application attachBaseContext and onCreate. Provider onCreate takes precedence over Application onCreate. The Application has been created successfully, and the context in the Provider is the object of the Application

Note: When using ContentProvider initialization, we need to note a few things: In the Manifest, set the ContentProvider to the authorities, which is equivalent to the identity of the ContentProvider, and cannot be repeated. In order to ensure that the ContentProvider is not repeated, the authorities should not be hardcode. Instead, use the following approach: AppID + XXX

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

As you can see from the ActivityThread handleBindApplication method, the installContentProviders method is called first. Then call mInstrumentation callApplicationOnCreate method.

Principle 2.

Now that we know why we don’t need to initialize the Application, let’s clarify how canary works. We click on AppWatcher via this initialization code

@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. Initialize various listeners such as activities, fragments, etc. */ 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)// Poll once at a specified time}, isEnabled = {true})Copy the code

In the code, we can see that there is an objectWatcher. What is that objectWatcher? In fact, it is the base class for all listeners to process. Other listeners call objectWatcher final processing by listening to the page life cycle, such as 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)
  }
}
Copy the code

ObjectWatcher ObjectWatcher ObjectWatcher 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>() private val queue = ReferenceQueue<Any> @synchronized Override fun expectexpectweaklyreachable (watchedObject: Any, description: String) {if (! isEnabled()) { return } removeWeaklyReachableObjects() val key = UUID.randomUUID() .toString() val watchUptimeMillis = clock.uptimeMillis() val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, 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 / / add the key value value checkRetainedExecutor. Execute {// implement polling via mainhandler.postdelayed () to find out if the object in the queue is weak reference, if it is, it will be removed, otherwise memory will be leaking, 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) }Copy the code

conclusion

A fierce analysis such as tiger, a look at the salary 250. Finally, summarize the principle:

  • The canary is first initialized via the ContentProvider
  • The various listeners are then initialized
  • To add an object to a weak reference queue, either by destroying the listener page or by actively adding it to the listener
  • The timed polling and queue.poll() methods are used to find out whether an object is weak or not. If the object has not become weak after a certain period of time, a leak is reported; otherwise, the queue is removed

There are also analysis of the causes of memory leaks and other principles to see the back to write