Question origin

Everyone is now using LeakCanary to detect memory leaks. The introduction is relatively simple.

Before LeakCanary 2.0

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

And onCreate in Application:

LeakCanary.install(this);
Copy the code

After LeakCanary 2.0, LeakCanary. Install (this) does not have to be written manually by the developer, as contentProvider is used to do it automatically

internal sealed class AppWatcherInstaller : ContentProvider() { override fun onCreate(): Boolean { val application = context!! .applicationContext as Application InternalAppWatcher.install(application)return true}}Copy the code

Once installed, you’ll see a Leaks desktop icon generated

To explore problems

Click on the Leaks icon and dump the Top Activity or other tools to see if the activity is DisplayLeakActivity. Go to Github and find the LeakCanary source code (Tag 1.5). A search for DisplayLeakActivity in the library can easily be found

<activity
        android:theme="@style/leak_canary_LeakCanary.Base"
        android:name=".internal.DisplayLeakActivity"
        android:process=":leakcanary"
        android:enabled="false"
        android:label="@string/leak_canary_display_activity_label"
        android:icon="@mipmap/leak_canary_icon"
        android:taskAffinity="com.squareup.leakcanary.${applicationId}"
        >
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
Copy the code

The original DisplayLeakActivity also defines the action android: name = “android. Intent. Action. MAIN” / > and < category The android: name = “android. Intent. The category. The LAUNCHER” / >, so also can appear on the application list. This is an implementation of LeakCanary 1.5, but how does LeakCanary 2.0 work

    <activity
        android:name="leakcanary.internal.activity.LeakActivity"
        android:icon="@mipmap/leak_canary_icon"
        android:label="@string/leak_canary_display_activity_label"
        android:taskAffinity="com.squareup.leakcanary.${applicationId}"
        android:theme="@style/leak_canary_LeakCanary.Base"
        />

    <activity-alias
        android:name="leakcanary.internal.activity.LeakLauncherActivity"
        android:enabled="@bool/leak_canary_add_launcher_icon"
        android:icon="@mipmap/leak_canary_icon"
        android:label="@string/leak_canary_display_activity_label"
        android:targetActivity="leakcanary.internal.activity.LeakActivity"
        android:taskAffinity="com.squareup.leakcanary.${applicationId}"
        android:theme="@style/leak_canary_LeakCanary.Base"
        >
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity-alias>

Copy the code

As you can see, the way LeakCanary generates ICONS has been changed to use the activity-alias implementation. As the name suggests, activity-alias can specify a targetActivity, which is then used as an alias for the targetActivity, often used by Launcher discriminants. Note that this activity-alias is only used as a tag, there is no class, and it is enabled via the Enabled attribute. Regarding the use of activity-alias, which can be used to dynamically replace ICONS, see this article: I changed one line of code in the last release

LeakCanary Principle summary

Let’s start with an architecture diagram (from LeakCanary source code analysis) :