Leakcanary: Improve documentation on how to set up custom LeakCanary configuration

Created on 3 Dec 2020  ·  13Comments  ·  Source: square/leakcanary

This mostly a help request, maybe a bug but I don't think so. Documentation request felt like the best fit.

So we have some configuration that looks like this:

class LeakCanaryConfigurator {
    init {
        val watchFragments = when (BuildType.current) {
            BuildType.DEBUG -> true
            else -> false
        }

        leakcanary.AppWatcher.config = leakcanary.AppWatcher.config.copy(
            watchFragments = watchFragments,
            watchFragmentViews = watchFragments,
            watchDurationMillis = TimeUnit.SECONDS.toMillis(8)
        )
    }
}

and we use it like this:

    override fun onCreate() {
        super.onCreate()
        when (BuildType.current) {
            BuildType.DEBUG, BuildType.QA -> LeakCanaryConfigurator()
            BuildType.QA_RELEASE, BuildType.RELEASE -> Unit
        }
...

This is the onCreate on our main component. And this is all fine and dandy and works fine...when we have leakcanary in our build! We have leakcanary setup to be in all our builds _except_ our release builds. Our gradle configuration looks like this:

    // Leak canary
    debugImplementation parent.ext.leakCanary
    qaImplementation parent.ext.leakCanary
    qaReleaseImplementation parent.ext.leakCanary

So when we go to build our release app, we get this error:

> Task :app:compileReleaseKotlin
e: /builds/gc.com/androdyssey/app/src/main/java/com/gc/androdyssey/OdysseyApplication.kt: (46, 29): Unresolved reference: leakcanary
e: /builds/gc.com/androdyssey/app/src/main/java/com/gc/androdyssey/OdysseyApplication.kt: (46, 51): Variable expected
e: /builds/gc.com/androdyssey/app/src/main/java/com/gc/androdyssey/OdysseyApplication.kt: (46, 60): Unresolved reference: leakcanary

which makes sense I suppose.
We were able to just comment out the configuration to get our release out, but it stands to reason about how we should set this configuration up?
I tried to follow this documentation, but I guess I don't know how to set up separate "Debug" and "Release" Application classes so we can build a project without leakcanary being present.

Please let me know if their is other information I can provide and please advise! Thanks.

documentation

All 13 comments

Thanks for the detailed question!

The key idea is to create dedicated source sets for the build flavors that depend on LeakCanary: https://developer.android.com/studio/build/build-variants#sourcesets . Typically you would have MyApplication in your main subset, then MyInternalBuildApplication extends MyApplication in your debug / qa sourceset, and in that class you would add the leakcanary configuration.

Does that make sense? Any suggestion on how we can update the docs to make this more clear?

Hmmm, yeah okay. Totally new concept to me, however. I have always just operated in main 😅

Do I just create a MyInternalBuildApplication extends MyApplication in app/src/debug/, have my configuration there and it just works?

If you have any links to something step by step (Source sets for dummies?) or examples, that'd be great. Or I can try to digest the docs you linked.

Maybe this is outside the scope of you helping me so feel free to tell me to just go figure it out haha

I'd recommend reading the doc I linked :)

You can also run ./gradlew app:sourceSet (replace app with whatever module name) which lists all build configuration and shows the related additional source folder.

Once you know where to add code, yeah you'll want to create a subclass of your application class AND you need to register it in the manifest. That means creating app/src/debug/AndroidManifest.xml and redefining just the application with the name attribute in there.

Okay, thanks, I appreciate it!

Reopening :) .

It's clear from your question that the doc isn't obvious, so we need to make it better or point to a concrete example.

Okay, I can go through the process and post my process. I think our project is fairly traditional in its setup so the steps it takes me to get there should be reusable. I don't have any other concrete recommendations ATM

That'd be great, thx!

Okay, I'm back. Hoping you can help me out. I think I have set things up _semi_ correctly. But I keep getting java.lang.RuntimeException: Unable to instantiate application com.gc.androdyssey.DebugOdysseyApplication: java.lang.ClassNotFoundException: Didn't find class "com.gc.androdyssey.DebugOdysseyApplication" and the application doesn't run.

My src/debug/AndroidManifest.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.gc.androdyssey">

    <application
        android:name=".DebugOdysseyApplication"
        tools:replace="android:name"
        >

    </application>

</manifest>

I have made the changes to build.gradle with this:

    sourceSets {
...

        debug {
            java.srcDirs = ['src/debug/java', 'src/main/java']
        }
    }

My debug/java/com.gc.androdyssey/DebugOdysseyApplication looks like:

class DebugOdysseyApplication : OdysseyApplication() {}

Where OdysseyApplication is my main, normal application class.

So it's picking up the correct debug Manifest file and looking for the DebugOdysseyApplication correctly...but for some reason the class is not getting built into the APK. I have
✅ Cleaned the project
✅ Synced gradle
✅ I am running the debug build variant.

What weird thing am I missing? I feel like I am very close

Just to make sure this is a typo: you wrote debug/java/com.gc.androdyssey/DebugOdysseyApplication but meant src/debug/java/com/gc/androdyssey/DebugOdysseyApplication.java right? ie adding src at the beginning and one folder per package.

Yep, that was a typo. The location you have noted is correct. The only difference is that I am using a .kt file.

The only thing is, you shouldn't need to do this:

    sourceSets {
...

        debug {
            java.srcDirs = ['src/debug/java', 'src/main/java']
        }
    }

Creating the folder should be enough if you have the debug build type.

Sorry for the late reply.

So we found the source set path a little complex and I was not able to get it working 😔

We ended up adding
releaseCompileOnly parent.ext.leakCanary
To our gradle file so now our configuration looks like:

    releaseCompileOnly parent.ext.leakCanary
    debugImplementation parent.ext.leakCanary
    qaImplementation parent.ext.leakCanary
    qaReleaseImplementation parent.ext.leakCanary

This worked for us. Compiled and since we actually don't run leak canary in production, the app doesn't crash at runtime. Maybe this isn't the defacto way of doing it, but it works for us 🤷‍♂️

I wouldn't write that in the doc, but I'm glad you've got it sorted out :)

Was this page helpful?
0 / 5 - 0 ratings