read the fucking source code

preface

This article does not contain the code to analyze the Haha library used by LeakCanary.

LeakCanary website

This article is based on: Leakcanary – Android :2.7

Simple to use is just a sentence, even the initialization code does not need you to write.

dependencies {
  debugImplementation 'com. Squareup. Leakcanary: leakcanary - android: 2.7'
}
Copy the code

LeakCanary Initialization analysis

The leakCanary project directory is shown above, and the initialization code is located at leakcanary-Android-process. The manifest file is as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.squareup.leakcanary.objectwatcher" >

    <uses-sdk android:minSdkVersion="14" />

    <application>
    	<! Init with provider -->
        <provider
            android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
            android:authorities="${applicationId}.leakcanary-installer"
            android:enabled="@bool/leak_canary_watcher_auto_install"
            android:exported="false" />
    </application>

</manifest>
Copy the code

One more thing here is that when AMS starts an app, it calls provider.oncreate and then it calls application.oncreate. Let me learn from LeakCanary again. OnCreate, ContentProvider. OnCreate, activity.oncreate

//AppWatcherInstaller.kt
internal sealed class AppWatcherInstaller : ContentProvider() {
  // The inner class can be used to construct two different providers instantly, which can be switched, tested, etc
  internal class MainProcess : AppWatcherInstaller(a)internal class LeakCanaryProcess : AppWatcherInstaller(a)override fun onCreate(a): Boolean {
    valapplication = context!! .applicationContextas Application
    AppWatcher.manualInstall(application)
    return true
  }
Copy the code
//ObjectWatcher.kt
class ObjectWatcher constructor(
  private val clock: Clock,
  Executor is clearly a JDK class that encapsulates thread scheduling
  private val checkRetainedExecutor: Executor,
  private val isEnabled: () -> Boolean = { true})//AppWatcher.kt
object AppWatcher {

//ObjectWatcher objects will be specified later. Let's start by looking at a property called checkRetainedExecutor
val objectWatcher = ObjectWatcher(
   clock = { SystemClock.uptimeMillis() },
   checkRetainedExecutor = {
    //internal val mainHandler by lazy { Handler(Looper.getMainLooper()) }
    // Get the android mainline handler here
    // There is a reason why the main thread is used here
     mainHandler.postDelayed(it, retainedDelayMillis)
   },
   isEnabled = { true})fun manualInstall(
    application: Application,
    retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
    watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
  ) {
    / /... slightly
    // Register some listeners
    LeakCanaryDelegate.loadLeakCanary(application)

    // Let's look at the set
    watchersToInstall.forEach {
      it.install()
    }
  }
  
   fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
  	// Each collection object represents a class that can monitor for memory leaks
    return listOf(
      / / monitor the Activity
      ActivityWatcher(application, reachabilityWatcher),
      / / fragments and the viewmodel
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      / / monitor the view
      RootViewWatcher(reachabilityWatcher),
      / / monitor service
      ServiceWatcher(reachabilityWatcher)
    )
  }	
}  
Copy the code

This is done by calling install to the four collection objects above.

ActivityWatcher

ActivityWatcher monitors Activity related leaks. Before we get to the bottom of this, let’s add a small fact.

ReferenceQueue use

ReferenceQueue is referenced in relation to which objects we know are recycled.


public class JavaMain {
    static String smg = new String("nihao");

    public static void main(String[] args) throws InterruptedException {

        ReferenceQueue<String> queue = new ReferenceQueue<>();
        // When a WeakReference object is reclaimed, the reference is put in the queue
        Reference<String> reference = new WeakReference<>(smg, queue);
        
        Reference<? extends String> poll = queue.poll();
	    // Prints null because the object is referenced by SMG
	    System.out.println(poll);
        smg = null;
       
        On Android, use Runtime.getruntime ().gc(); Specific reasons will be explained later
        System.gc();
        TimeUnit.SECONDS.sleep(1);
        / / output Java. Lang. Ref. 15 db9742 WeakReference @poll = queue.poll(); System.out.println(poll); }}Copy the code

The above is a small example, please be sure to understand. In addition, if WeakReference holds a reference to a constant pool, it will not be reclaimed. As follows:

public class JavaMain {
    // New String("nihao")
    // SMG points to the constant pool nihao
    static String smg = "nihao";

    public static void main(String[] args) throws InterruptedException {

        ReferenceQueue<String> queue = new ReferenceQueue<>();
        Reference<String> reference = new WeakReference<>(smg, queue);
        Reference<? extends String> poll = queue.poll();
        / / output is null
        System.out.println(poll);
        smg = null;
        On Android, use Runtime.getruntime ().gc(); Specific reasons will be explained later
        System.gc();
        TimeUnit.SECONDS.sleep(1);
       
        poll = queue.poll();
        // Output null. Constant pool objects are not easily recycledSystem.out.println(poll); }}Copy the code

The core idea of ActivityWatcher is to put the Activity into a WeakReference when the Activity is destroyed, and the WeakReference will pass in a ReferenceQueue and judge the Referenc after waiting for a certain amount of time Is there a new element in eQueue?

ActivityWatcher

//ActivityWatcher.kt
class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
    	// Register the Activity's destory callback with the Application object.
    	/ / point reachabilityWatcher AppWatcher objectWatcher
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback")}}// The initialization code is called
  override fun install(a) {
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

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

Copy the code
class ObjectWatcher constructor(
 private val clock: Clock.private val checkRetainedExecutor: Executor/ / points tomainThread ohprivate val isEnabled: () - >Boolean = { true{})The watchedObjects reference needs to be removed if queue.pool returns an object
  private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
  Queue stores casual to detect, and works with watchedObjects to detect leaks
  private val queue = ReferenceQueue<Any>()

  @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) {
  
    if(! isEnabled()) {return
    }
    
    // Loop queue.pool until null is returned, cleaning up the watchedObjects that can return data
    removeWeaklyReachableObjects()

    val key = UUID.randomUUID()
      .toString()
    val watchUptimeMillis = clock.uptimeMillis()
	// Put the current Activity into the reference object, and notice that queue is passed in
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    
	// Put it in an array
    watchedObjects[key] = reference
    //checkRetainedExecutor is the main thread, execute will drop the task to the queue, so the execution will be delayed.
    // Why checkRetainedExecutor is the main thread? Because the Activity call destroy is not immediately reclaimed,
    // Android does some cleanup after destroy returns
    checkRetainedExecutor.execute {
    // Go further
      moveToRetained(key)
    }
  }
  private fun removeWeaklyReachableObjects(a) {
    Queue. Pool returns the object, so watchedObjects does not need to save the monitor object to prove that the object was recycled
    var ref: KeyedWeakReference?
    do {
      ref = queue.poll() as KeyedWeakReference?
      if(ref ! =null) {
        watchedObjects.remove(ref.key)
      }
    } while(ref ! =null)}}Copy the code

We dropped an event onto the main thread and waited for moveToRetained, at which point the base Activity was reclaimed

   private fun moveToRetained(key: String) {
  	// Remove the reclaimed object
    removeWeaklyReachableObjects()
    
    val retainedRef = watchedObjects[key]
    / / if not removeWeaklyReachableObjects remove retainedRef so prove to be no recovery
    if(retainedRef ! =null) {
      retainedRef.retainedUptimeMillis = clock.uptimeMillis()
      / / onObjectRetainedListeners in LeakCanaryDelegate. LoadLeakCanary (application) is set
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }	
Copy the code
//InternalLeakCanary.kt
// There are two interfaces inherited:
// (Application) -> Unit
// OnObjectRetainedListener
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
  // Run to here
  override fun onObjectRetained(a) = scheduleRetainedObjectCheck()

  fun scheduleRetainedObjectCheck(a) {
  	// count as true
    if (this::heapDumpTrigger.isInitialized) {
      //heapDumpTrigger is heapDumpTrigger, which is again forbidden to check whether the object is not released
      heapDumpTrigger.scheduleRetainedObjectCheck()
    }
  }
}
Copy the code
//HeapDumpTrigger.kt
 fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      return
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
   	//backgroundHandler is a child thread
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      checkRetainedObjects()
    }, delayMillis)
  }
 private fun checkRetainedObjects(a) {
   		// Again strictly check whether there is leakage,
   		// Get the number of lives
        var retainedReferenceCount = objectWatcher.retainedObjectCount
		
        if (retainedReferenceCount > 0) {
          // Fetch the number of lives once gc is triggered
          gcTrigger.runGc()
          retainedReferenceCount = objectWatcher.retainedObjectCount
        }
        / /... slightly
}        
Copy the code
 object Default : GcTrigger {
    override fun runGc(a) {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perform a gc.
      System.gc() does not always execute gc, runtime.gc () is more likely to execute gc
      Runtime.getRuntime()
        .gc()
      enqueueReferences()
      System.runFinalization()
    }

    private fun enqueueReferences(a) {
      // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100)}catch (e: InterruptedException) {
        throw AssertionError()
      }
    }
  }
Copy the code

We don’t analyze after confirming leaks, we look at how other leaks are detected, right

FragmentAndViewModelWatcher

class FragmentAndViewModelWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit> ()/ / slightly AndroidXFragmentDestroyWatcher into fragmentDestroyWatchers here
	
    fragmentDestroyWatchers
  }

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?). {
      // Notice that an observation is placed when onCreate is called
      / / store AndroidXFragmentDestroyWatcher fragmentDestroyWatchers
        for (watcher in fragmentDestroyWatchers) {
          / / call AndroidXFragmentDestroyWatcher. Invoke.
          //	
          watcher(activity)
        }
      }
    }

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

  override fun uninstall(a) {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  } 
}
Copy the code
//AndroidXFragmentDestroyWatcher.kt
internal class AndroidXFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {

  override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      / / registerFragmentLifecycleCallbacks can listen to each fragment of add and instances
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      //viewModel ()
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
  
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?). {
      // ViewModel
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if(view ! =null) {
      	// Check whether the view is held
        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
    ) {
      // Check whether the fragment is held. I'm not going to repeat myself here
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback")}}}Copy the code

ViewModelClearedWatcher

The previous section we saw in the fragments of the create function call ViewModelClearedWatcher. Install (fragments, reachabilityWatcher) to monitor the viewmodel leak. Make sure you understand the viewModel basics:

ViewModel source code analysis

internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel(a) {

  private val viewModelMap: Map<String, ViewModel>?

  init {
	  // The ViewModelStore contains a mMap object, which holds all viewModels
      val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
      mMapField.isAccessible = true
	 // Get the Fragment ViewModelStoreOwner instance to get the mMap object
	 // To get all the viewModels
      mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
    } catch (ignored: Exception) {
      null}}override fun onCleared(a) {
  	// Start reclaiming viewModel
  	// Walk through all viewModels and check for leaksviewModelMap? .values? .forEach { viewModel -> reachabilityWatcher.expectWeaklyReachable( viewModel,"${viewModel::class.java.name} received ViewModel#onCleared() callback"
      )
    }
  }

  companion object {
    fun install( storeOwner: ViewModelStoreOwner, reachabilityWatcher: ReachabilityWatcher ) {
      // Notice the factory passed in here, but what is passed in is creating the ViewModelClearedWatcher objectval provider = ViewModelProvider(storeOwner, object : Factory { override fun <T : ViewModel? > create(modelClass: Class<T>): T = ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T })/ / ViewModelClearedWatcher is a spy viewmodel, mainly in order to monitor the oncleared callback
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
}
Copy the code

ServiceWatcher

Monitoring the Destroy code for a Service can be a bit complicated. Design reflection, and the service startup process. You need to know something about plug-ins. This article may require some basic knowledge of handler, see blogger: Handler

Knowledge reserves

The creation and destruction of our four major components are transferred from AMS to our App’s ActivityThread.MH through Binder(cross-process IPC).

Mh is our Handler, so we can reflect the MH, and then set an mCallback object to the Handler to intercept all AMS messages. MCallback returns false and continues to be processed by the Handler.

public final class ActivityThread {
	    final H mH = new H();
	private class H extends Handler {}}Copy the code

We setmCallbackafter

Let’s take a last lookLeakCanaryThe relevant code


  private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread")}//ActivityThread has a static currentActivityThread. Save its own instance
 private val activityThreadInstance by lazy {	
    activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!!!! }private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?). ->Handler.Callback?). {
    / / activityThreadClass for Class. Class.forname (" android. App. ActivityThread ")
    val mHField =
      activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
    // Get attributes, where mHField[XXXX] is kotlin syntax is not confused
    val mH = mHField[activityThreadInstance] as Handler
	/ / find Handler. MCallback
    val mCallbackField =
      Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
    // Get the mCallback instance
    val mCallback = mCallbackField[mH] as Handler.Callback?
    // Replace it with its own handler. Callback, which is internally forwarded to the original mCallback
    mCallbackField[mH] = swap(mCallback)
  }
Copy the code

Service and a related function ActivityManagerProxy. ServiceDoneExecuting, When the service has been created (callback after the oncreate), back to call ActivityManagerProxy. ServiceDoneExecuting. When the service is destroyed (AMS issued news, and after the callback onDestroy) callback ActivityManagerProxy. ServiceDoneExecuting

Service startup process source code read

So we have two hook points that can be used to detect being destroyed.

/ / because are simple operation, the reader can category swapActivityThreadHandlerCallback above
private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
    val singletonClass = Class.forName("android.util.Singleton")
    val mInstanceField =
      singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }

    val singletonGetMethod = singletonClass.getDeclaredMethod("get")

    val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      "android.app.ActivityManager" to "IActivityManagerSingleton"
    } else {
      "android.app.ActivityManagerNative" to "gDefault"
    }

    val activityManagerClass = Class.forName(className)
    val activityManagerSingletonField =
      activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
    val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]

    // Calling get() instead of reading from the field directly to ensure the singleton is
    // created.
    val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)

    val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
    mInstanceField[activityManagerSingletonInstance] =
      swap(iActivityManagerInterface, activityManagerInstance!!)
  }
Copy the code

Finally, the mServices for ActivityThread holds all app services

//ActivityThread.java
class ActivityThread{
  
 final ArrayMap<IBinder, Service> mServices
            = new ArrayMap<IBinder, Service>();
}
Copy the code

We combine the following with a simple look at ServiceWatcher.

//ServiceWatcher.java
  // Store the service to be observed
  private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()

  private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread")}private val activityThreadInstance by lazy {
    activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!!!! }// Store all app services
  private val activityThreadServices by lazy {
    val mServicesField =
      activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }

    @Suppress("UNCHECKED_CAST")
    mServicesField[activityThreadInstance] as Map<IBinder, Service>
  }
  
override fun install(a) {
    checkMainThread()
    
    try {
    
      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback
          }
        }
        Handler.Callback { msg ->
  		  // Call the stopService function
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            //activityThreadServices must not be empty
            // So onServicePreDestroy will put the Service in servicesToBeDestroyed internally
            ServiceDoneExecuting is a callbackactivityThreadServices[key]? .let { onServicePreDestroy(key, it) } } mCallback? .handleMessage(msg) ? :false}}/ / servicesToBeDestroyed related hooks
      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
            //servicesToBeDestroyed will be called back when it is created and also when it is destroyed
            // Add elements when servicesToBeDestroyed above.
            // So servicesToBeDestroyed contains the service to be detected and calls onServiceDestroyed directly to check if the service is damaged
            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"}}}Copy the code