LeakCanary 2 is a major rewrite. High level changes:
- New heap analyzer, reimplemented from scratch to use 10 times less memory (see Shark).
- APIs updated to simplify configuration and provide access to the new heap analyzer.
- Internals rewritten to 100% Kotlin.
- Multiple leaks detected in one analysis, grouped per leak type
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
// Optional, if you use support library fragments:
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
}
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:{{ leak_canary.release }}'
}
- The
leakcanary-android-no-op
artifact is gone. If you have compile errors, see below.- Question: if there's no no-op anymore, how do I ensure none of this runs during release builds?
- Answer: as long as you add
leakcanary-android
asdebugImplementation
, there won't be any code referencing LeakCanary in your release builds.
- LeakCanary does not depend on the support library anymore, and it doesn't depend on AndroidX either.
- Detection of AndroidX fragments is automatic if you have the AndroidX fragments dependency.
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}
There is no more code for default setup.
- LeakCanary auto installs itself
- LeakCanary analysis now runs in the main process so there is no need to call
LeakCanary.isInAnalyzerProcess()
.
val refWatcher: RefWatcher = LeakCanary.installedRefWatcher()
val objectWatcher: ObjectWatcher = AppWatcher.objectWatcher
If you were using RefWatcher
in non debug code, you now get a compile error because the no-op artifact is gone. ObjectWatcher now lives in the object-watcher
artifact, which is suitable for release builds. You have two options:
dependencies {
implementation 'com.squareup.leakcanary:leakcanary-object-watcher-android:{{ leak_canary.release }}'
}
- It will automatically keep weak references to destroyed activities, fragments, and any instance you pass to AppWatcher.objectWatcher.
- It will not trigger heap dumps or anything else that LeakCanary does.
- It's very little code and should have a no impact on your release app.
- You can use it to count how many objects are retained, for example to add metadata to OutOfMemoryError crashes:
val retainedObjectCount = AppWatcher.objectWatcher.retainedObjectCount
// In shared code
interface MaybeObjectWatcher {
fun watch(watchedObject: Any, description: String)
object None : MaybeObjectWatcher {
override fun watch(watchedObject: Any, description: String) {
}
}
}
// In debug code
class RealObjectWatcher : MaybeObjectWatcher {
override fun watch(watchedObject: Any, description: String) {
AppWatcher.objectWatcher.watch(watchedObject, description)
}
}
Use MaybeObjectWatcher.None
in release code and RealObjectWatcher
in debug code.
public class DebugExampleApplication extends ExampleApplication {
@Override protected void installLeakCanary() {
RefWatcher refWatcher = LeakCanary.refWatcher(this)
.watchActivities(false)
.buildAndInstall();
}
}
AppWatcher 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 is in charge of taking heap dumps and analyzing them. Its configuration can be updated at any time by replacing LeakCanary.config:
disableLeakCanaryButton.setOnClickListener {
LeakCanary.config = LeakCanary.config.copy(dumpHeap = false)
}
In your build.gradle
file:
dependencies {
androidTestImplementation "com.squareup.leakcanary:leakcanary-android-instrumentation:${leakCanaryVersion}"
}
android {
defaultConfig {
// ...
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "listener", "com.squareup.leakcanary.FailTestOnLeakRunListener"
}
}
In your test Application
class:
public class InstrumentationTestExampleApplication extends DebugExampleApplication {
@Override protected void installLeakCanary() {
InstrumentationLeakDetector.instrumentationRefWatcher(this)
.buildAndInstall();
}
}
Remove all the previous test related leak detection code then follow Leak detection in UI tests.
public class LeakUploadService extends DisplayLeakService {
@Override protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
// TODO Upload result to server
}
}
RefWatcher refWatcher = LeakCanary.refWatcher(this)
.listenerServiceClass(LeakUploadService.class)
.buildAndInstall();
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:name="com.example.DebugExampleApplication">
<service android:name="com.example.LeakUploadService" />
</application>
</manifest>
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)
}
}
class DebugExampleApplication : ExampleApplication() {
override fun onCreate() {
super.onCreate()
LeakCanary.config = LeakCanary.config.copy(
onHeapAnalyzedListener = LeakUploader()
)
}
}
ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults()
.staticField("com.samsing.SomeSingleton", "sContext")
.build();
RefWatcher refWatcher = LeakCanary.refWatcher(this)
.excludedRefs(excludedRefs)
.buildAndInstall();
}
LeakCanary.config = LeakCanary.config.copy(
referenceMatchers = AndroidReferenceMatchers.appDefaults +
AndroidReferenceMatchers.staticFieldLeak(
"com.samsing.SomeSingleton",
"sContext"
)
)
!!! info
There is no equivalent API to ExcludedRefs.Builder.clazz()
because it led to abuses. Instead see Ignoring specific activities or fragment classes.
All public APIs were in com.squareup.leakcanary.*
All public APIs are in leakcanary.*