1LeakCanary 2 is a major rewrite. High level changes: 2 3* New heap analyzer, reimplemented from scratch to use 10 times less memory ([see Shark](shark.md)). 4* APIs updated to simplify configuration and provide access to the new heap analyzer. 5* Internals rewritten to 100% Kotlin. 6* Multiple leaks detected in one analysis, grouped per leak type 7 8## Dependencies 9 10### Before 11 12```groovy 13dependencies { 14 debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' 15 releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' 16 // Optional, if you use support library fragments: 17 debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' 18} 19``` 20 21### Now 22 23```groovy 24dependencies { 25 debugImplementation 'com.squareup.leakcanary:leakcanary-android:{{ leak_canary.release }}' 26} 27``` 28 29### Worth noting 30 31* The `leakcanary-android-no-op` artifact is gone. If you have compile errors, see below. 32 * **Question**: if there's no no-op anymore, how do I ensure none of this runs during release builds? 33 * **Answer**: as long as you add `leakcanary-android` as `debugImplementation`, there won't be any code referencing LeakCanary in your release builds. 34* LeakCanary does not depend on the support library anymore, and it doesn't depend on AndroidX either. 35* Detection of AndroidX fragments is automatic if you have the AndroidX fragments dependency. 36 37## Default setup code 38 39### Before 40 41```java 42public class ExampleApplication extends Application { 43 44 @Override public void onCreate() { 45 super.onCreate(); 46 if (LeakCanary.isInAnalyzerProcess(this)) { 47 // This process is dedicated to LeakCanary for heap analysis. 48 // You should not init your app in this process. 49 return; 50 } 51 LeakCanary.install(this); 52 // Normal app init code... 53 } 54} 55``` 56 57### Now 58 59There is no more code for default setup. 60 61### Worth noting 62 63* LeakCanary auto installs itself 64* LeakCanary analysis now runs in the main process so there is no need to call `LeakCanary.isInAnalyzerProcess()`. 65 66## Retrieve the RefWatcher 67 68### Before 69 70```kotlin 71val refWatcher: RefWatcher = LeakCanary.installedRefWatcher() 72``` 73 74### Now 75 76```kotlin 77val objectWatcher: ObjectWatcher = AppWatcher.objectWatcher 78``` 79 80## Compile errors because RefWatcher is used in release code 81 82If you were using `RefWatcher` in non debug code, you now get a compile error because the no-op artifact is gone. [ObjectWatcher](/leakcanary/api/leakcanary-object-watcher/leakcanary/-object-watcher/) now lives in the `object-watcher` artifact, which is suitable for release builds. You have two options: 83 84### Option 1: Add `object-watcher-android` to release builds. 85 86```groovy 87dependencies { 88 implementation 'com.squareup.leakcanary:leakcanary-object-watcher-android:{{ leak_canary.release }}' 89} 90``` 91 92* It will automatically keep weak references to destroyed activities, fragments, and any instance you pass to [AppWatcher.objectWatcher](/leakcanary/api/leakcanary-object-watcher-android/leakcanary/-app-watcher/object-watcher/). 93* It will not trigger heap dumps or anything else that LeakCanary does. 94* It's very little code and should have a no impact on your release app. 95* You can use it to count how many objects are retained, for example to add metadata to OutOfMemoryError crashes: 96 97```kotlin 98val retainedObjectCount = AppWatcher.objectWatcher.retainedObjectCount 99``` 100 101### Option 2: Make your own `ObjectWatcher` interface 102 103```kotlin 104// In shared code 105interface MaybeObjectWatcher { 106 fun watch(watchedObject: Any, description: String) 107 108 object None : MaybeObjectWatcher { 109 override fun watch(watchedObject: Any, description: String) { 110 } 111 } 112} 113 114// In debug code 115class RealObjectWatcher : MaybeObjectWatcher { 116 override fun watch(watchedObject: Any, description: String) { 117 AppWatcher.objectWatcher.watch(watchedObject, description) 118 } 119} 120``` 121 122Use `MaybeObjectWatcher.None` in release code and `RealObjectWatcher` in debug code. 123 124## Configuring LeakCanary 125 126### Before 127 128```java 129public class DebugExampleApplication extends ExampleApplication { 130 131 @Override protected void installLeakCanary() { 132 RefWatcher refWatcher = LeakCanary.refWatcher(this) 133 .watchActivities(false) 134 .buildAndInstall(); 135 } 136} 137``` 138 139### Now 140 141AppWatcher is in charge of detecting retained objects. Its configuration can be updated at any time by replacing [AppWatcher.config](/leakcanary/api/leakcanary-object-watcher-android/leakcanary/-app-watcher/config/): 142 143```kotlin 144class DebugExampleApplication : ExampleApplication() { 145 146 override fun onCreate() { 147 super.onCreate() 148 AppWatcher.config = AppWatcher.config.copy(watchFragmentViews = false) 149 } 150} 151``` 152 153LeakCanary is in charge of taking heap dumps and analyzing them. Its configuration can be updated at any time by replacing [LeakCanary.config](/leakcanary/api/leakcanary-android-core/leakcanary/-leak-canary/config/): 154 155```kotlin 156disableLeakCanaryButton.setOnClickListener { 157 LeakCanary.config = LeakCanary.config.copy(dumpHeap = false) 158} 159``` 160 161## Running LeakCanary in instrumentation tests 162 163### Before 164 165In your `build.gradle` file: 166 167```groovy 168dependencies { 169 androidTestImplementation "com.squareup.leakcanary:leakcanary-android-instrumentation:${leakCanaryVersion}" 170} 171 172android { 173 defaultConfig { 174 // ... 175 176 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 177 testInstrumentationRunnerArgument "listener", "com.squareup.leakcanary.FailTestOnLeakRunListener" 178 } 179} 180``` 181 182In your test `Application` class: 183 184```java 185public class InstrumentationTestExampleApplication extends DebugExampleApplication { 186 @Override protected void installLeakCanary() { 187 InstrumentationLeakDetector.instrumentationRefWatcher(this) 188 .buildAndInstall(); 189 } 190} 191``` 192 193### Now 194 195Remove all the previous test related leak detection code then follow 196[Leak detection in UI tests](ui-tests.md). 197 198## Analysis listener / uploading to a server 199 200### Before 201 202 203```java 204public class LeakUploadService extends DisplayLeakService { 205 @Override protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) { 206 // TODO Upload result to server 207 } 208} 209``` 210 211```java 212RefWatcher refWatcher = LeakCanary.refWatcher(this) 213 .listenerServiceClass(LeakUploadService.class) 214 .buildAndInstall(); 215``` 216 217```xml 218<?xml version="1.0" encoding="utf-8"?> 219<manifest xmlns:android="http://schemas.android.com/apk/res/android"> 220 <application android:name="com.example.DebugExampleApplication"> 221 <service android:name="com.example.LeakUploadService" /> 222 </application> 223</manifest> 224``` 225 226### Now 227 228```Kotlin 229class LeakUploader : OnHeapAnalyzedListener { 230 231 val defaultListener = DefaultOnHeapAnalyzedListener.create() 232 233 override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) { 234 TODO("Upload heap analysis to server") 235 236 // Delegate to default behavior (notification and saving result) 237 defaultListener.onHeapAnalyzed(heapAnalysis) 238 } 239} 240 241class DebugExampleApplication : ExampleApplication() { 242 243 override fun onCreate() { 244 super.onCreate() 245 LeakCanary.config = LeakCanary.config.copy( 246 onHeapAnalyzedListener = LeakUploader() 247 ) 248 } 249} 250``` 251 252### Matching known library leaks 253 254### Before 255 256```java 257ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults() 258 .staticField("com.samsing.SomeSingleton", "sContext") 259 .build(); 260RefWatcher refWatcher = LeakCanary.refWatcher(this) 261 .excludedRefs(excludedRefs) 262 .buildAndInstall(); 263} 264``` 265 266### Now 267 268```kotlin 269LeakCanary.config = LeakCanary.config.copy( 270 referenceMatchers = AndroidReferenceMatchers.appDefaults + 271 AndroidReferenceMatchers.staticFieldLeak( 272 "com.samsing.SomeSingleton", 273 "sContext" 274 ) 275) 276``` 277 278!!! info 279 There is no equivalent API to `ExcludedRefs.Builder.clazz()` because it led to abuses. Instead see [Ignoring specific activities or fragment classes](recipes.md#ignoring-specific-activities-or-fragment-classes). 280 281## Public API packages 282 283### Before 284 285All public APIs were in `com.squareup.leakcanary.*` 286 287### Now 288 289All public APIs are in `leakcanary.*` 290