If you think a recipe might be missing or you‘re not sure that what you’re trying to achieve is possible with the current APIs, please file an issue. Your feedback help us make LeakCanary better for the entire community.
object-watcher-androidAppWatcher is in charge of detecting retained objects. Its configuration can be updated at any time by replacing AppWatcher.config:
class DebugExampleApplication : ExampleApplication() { override fun onCreate() { super.onCreate() AppWatcher.config = AppWatcher.config.copy(watchFragmentViews = false) } }
LeakCanary can be configured by replacing LeakCanary.config:
disableLeakCanaryButton.setOnClickListener { LeakCanary.config = LeakCanary.config.copy(dumpHeap = false) }
In your application, you may have other objects with a lifecycle, such as fragments, services, Dagger components, etc. Use AppWatcher.objectWatcher to watch instances that should be garbage collected:
class MyService : Service { // ... override fun onDestroy() { super.onDestroy() AppWatcher.objectWatcher.watch(this) } }
com.squareup.leakcanary:leakcanary-android should only be used in debug builds. It depends on com.squareup.leakcanary:object-watcher-android which you can use in production to track and count retained instances.
In your build.gradle:
dependencies { implementation 'com.squareup.leakcanary:object-watcher-android:2.0-alpha-2' }
In your leak reporting code:
val retainedInstanceCount = AppWatcher.objectWatcher.retainedObjectCount
Add the leakcanary-android-instrumentation dependency to your instrumentation tests:
androidTestImplementation "com.squareup.leakcanary:leakcanary-android-instrumentation:${leakCanaryVersion}"
Add the dedicated run listener to defaultConfig in your build.gradle:
android {
defaultConfig {
// ...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "listener", "leakcanary.FailTestOnLeakRunListener"
}
}
Run the instrumentation tests:
./gradlew leakcanary-android-sample:connectedCheck
You can extend FailTestOnLeakRunListener to customize the behavior.
The activity that displays leaks comes with a default icon and label, which you can change by providing R.mipmap.leak_canary_icon and R.string.leak_canary_display_activity_label in your app:
res/
mipmap-hdpi/
leak_canary_icon.png
mipmap-mdpi/
leak_canary_icon.png
mipmap-xhdpi/
leak_canary_icon.png
mipmap-xxhdpi/
leak_canary_icon.png
mipmap-xxxhdpi/
leak_canary_icon.png
mipmap-anydpi-v26/
leak_canary_icon.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="leak_canary_display_activity_label">MyLeaks</string> </resources>
You can change the default behavior to upload the analysis result to a server of your choosing.
Create a custom AnalysisResultListener that delegates to the default:
class LeakUploader : OnHeapAnalyzedListener { val defaultListener = DefaultOnHeapAnalyzedListener.create() override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) { TODO("Upload heap analysis to server") // Delegate to default behavior (notification and saving result) defaultListener.onHeapAnalyzed(heapAnalysis) } }
Set analysisResultListener on the LeakCanary config:
class DebugExampleApplication : ExampleApplication() { override fun onCreate() { super.onCreate() LeakCanary.config = LeakCanary.config.copy(onHeapAnalyzedListener = LeakUploader()) } }
Set LeakCanary.Config.referenceMatchers to a list that builds on top of AndroidReferenceMatchers.appDefaults:
class DebugExampleApplication : ExampleApplication() { override fun onCreate() { super.onCreate() LeakCanary.config = LeakCanary.config.copy( referenceMatchers = AndroidReferenceMatchers.appDefaults + AndroidReferenceMatchers.staticFieldLeak( className = "com.samsing.SomeSingleton", fieldName = "sContext", description = "SomeSingleton has a static field leaking a context.", patternApplies = { manufacturer == "Samsing" && sdkInt == 26 } ) ) } }
class DebugExampleApplication : ExampleApplication() { override fun onCreate() { super.onCreate() val addObjectIdLabel = ObjectInspector { reporter -> reporter.addLabel("Heap dump object id is ${reporter.heapObject.objectId}") } val singletonsInspector = AppSingletonInspector("com.example.MySingleton", "com.example.OtherSingleton") val mmvmInspector = ObjectInspector { reporter -> reporter.whenInstanceOf("com.mmvm.SomeViewModel") { instance -> val destroyedField = instance["com.mmvm.SomeViewModel", "destroyed"]!! if (destroyedField.value.asBoolean!!) { reportLeaking("SomeViewModel.destroyed is true") } else { reportNotLeaking("SomeViewModel.destroyed is false") } } } LeakCanary.config = LeakCanary.config.copy( objectInspectors = AndroidObjectInspectors.appDefaults + listOf(addObjectIdLabel, singletonsInspector, mmvmInspector) ) } }
You can setup LeakCanary to run in a specific product flavors of your app. For example, create:
android {
flavorDimensions "default"
productFlavors {
prod {
// ...
}
qa {
// ...
}
dev {
// ...
}
}
}
Then, define a custom configuration for the flavor for which you want to enable LeakCanary:
android {
// ...
}
configurations {
devDebugImplementation {}
}
You can now add the LeakCanary dependency for that configuration:
dependencies {
devDebugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
}