Skip to content

Commit

Permalink
Update LeakCanary to 2.10
Browse files Browse the repository at this point in the history
Also added the now required "description" parameter when watching instances.

Fixes uber#580
  • Loading branch information
pyricau authored and FranAguilera committed Aug 23, 2023
1 parent 85cf3b3 commit 04a7f08
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@
package com.uber.rib;

import android.app.Application;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import com.uber.rib.core.ActivityDelegate;
import com.uber.rib.core.HasActivityDelegate;
import com.uber.rib.core.RibRefWatcher;
import java.util.concurrent.TimeUnit;
import leakcanary.AppWatcher;

public class SampleApplication extends Application implements HasActivityDelegate {

Expand All @@ -31,24 +29,17 @@ public class SampleApplication extends Application implements HasActivityDelegat
public void onCreate() {
activityDelegate = new SampleActivityDelegate();
super.onCreate();
if (!LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis. You should not init your app in
// this process.
installLeakCanary();
}
installLeakCanary();
}

/** Install leak canary for both activities and RIBs. */
private void installLeakCanary() {
final RefWatcher refWatcher =
LeakCanary.refWatcher(this).watchDelay(2, TimeUnit.SECONDS).buildAndInstall();
LeakCanary.install(this);
RibRefWatcher.getInstance()
.setReferenceWatcher(
new RibRefWatcher.ReferenceWatcher() {
@Override
public void watch(Object object) {
refWatcher.watch(object);
public void watch(Object object, String description) {
AppWatcher.INSTANCE.getObjectWatcher().expectWeaklyReachable(object, description);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ kotlin = "1.8.20"
kotlinx-coroutines = "1.7.3"
ktfmt = "0.43"
ktlint = "0.48.2"
leakcanary = "1.5.4"
leakcanary = "2.10"
mockito = "4.6.1"
mockito-kotlin = "4.0.0"
motif = "0.3.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,30 @@ public open class RibRefWatcher {
referenceWatcher = watcher
}

@Deprecated(
"Add the description parameter",
replaceWith = ReplaceWith("watchDeletedObject(objectToWatch, description)"),
)
public open fun watchDeletedObject(
objectToWatch: Any?,
) {
watchDeletedObject(objectToWatch, "missing description")
}

/**
* Watch this object to verify it has no inbound references.
*
* @param objectToWatch the object to watch.
*/
public open fun watchDeletedObject(objectToWatch: Any?) {
public open fun watchDeletedObject(
objectToWatch: Any?,
description: String,
) {
if (objectToWatch == null) {
return
}
if (isLeakCanaryEnabled || uLeakEnabled) {
referenceWatcher?.watch(objectToWatch)
referenceWatcher?.watch(objectToWatch, description)
}
}

Expand Down Expand Up @@ -97,7 +110,7 @@ public open class RibRefWatcher {
*
* @param objectToWatch the object to watch.
*/
public fun watch(objectToWatch: Any)
public fun watch(objectToWatch: Any, description: String)

/**
* Method to pipe breadcrumbs into the Breadcrumb logger.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ protected constructor(
public open fun detachChild(childRouter: Router<*>) {
val isChildRemoved = children.remove(childRouter)
val interactor = childRouter.interactor
ribRefWatcher.watchDeletedObject(interactor)
ribRefWatcher.watchDeletedObject(
interactor,
"detached child router ${childRouter.javaClass.name}",
)
ribRefWatcher.logBreadcrumb(
"DETACHED",
childRouter.javaClass.simpleName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ class InteractorAndRouterTest {
val childInteractor = TestInteractorB()
val childRouter = TestRouterB(childInteractor, component)
router.attachChild(childRouter)
verify(ribRefWatcher, never()).watchDeletedObject(any())
verify(ribRefWatcher, never()).watchDeletedObject(any(), "")

// Action: Detach the child interactor.
router.detachChild(childRouter)

// Verify: the reference watcher observes the detached interactor and child.
verify(ribRefWatcher).watchDeletedObject(eq(childInteractor))
verify(ribRefWatcher).watchDeletedObject(eq(childInteractor), "")
}

@Test
Expand All @@ -207,13 +207,13 @@ class InteractorAndRouterTest {
}
val rootRouter = TestRouterB(component, TestInteractorB(), ribRefWatcher)
val child = addTwoNestedChildInteractors()
verify(ribRefWatcher, never()).watchDeletedObject(any())
verify(ribRefWatcher, never()).watchDeletedObject(any(), "")

// Action: Detach all child interactors.
rootRouter.detachChild(child)

// Verify: called four times. Twice for each interactor.
verify(ribRefWatcher, times(2)).watchDeletedObject(any())
verify(ribRefWatcher, times(2)).watchDeletedObject(any(), "")
}

private fun addTwoNestedChildInteractors(): Router<TestInteractorB> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ class RibRefWatcherTest {
fun watchDeletedObject_whenObjectIsNull_shouldDoNothing() {
ribRefWatcher.enableLeakCanary()
ribRefWatcher.setReferenceWatcher(referenceWatcher)
ribRefWatcher.watchDeletedObject(null)
ribRefWatcher.watchDeletedObject(null, "")
verifyNoInteractions(referenceWatcher)
}

@Test
fun watchDeletedObject_whenReferenceWatcherIsNull_shouldDoNothing() {
ribRefWatcher.enableLeakCanary()
ribRefWatcher.watchDeletedObject(Any())
ribRefWatcher.watchDeletedObject(Any(), "")
verifyNoInteractions(referenceWatcher)
}

Expand All @@ -52,30 +52,30 @@ class RibRefWatcherTest {
ribRefWatcher.enableLeakCanary()
val obj = Any()
ribRefWatcher.setReferenceWatcher(referenceWatcher)
ribRefWatcher.watchDeletedObject(obj)
verify(referenceWatcher).watch(obj)
ribRefWatcher.watchDeletedObject(obj, "")
verify(referenceWatcher).watch(obj, "")
}

@Test
fun watchDeletedObject_whenNonNullRefWithDisabledLeakCanary_shouldDoNothing() {
val obj = Any()
ribRefWatcher.setReferenceWatcher(referenceWatcher)
ribRefWatcher.watchDeletedObject(obj)
verify(referenceWatcher, never()).watch(obj)
ribRefWatcher.watchDeletedObject(obj, "")
verify(referenceWatcher, never()).watch(obj, "")
}

@Test
fun watchDeletedObject_whenObjectIsNullWithULeak_shouldDoNothing() {
ribRefWatcher.enableULeakLifecycleTracking()
ribRefWatcher.setReferenceWatcher(referenceWatcher)
ribRefWatcher.watchDeletedObject(null)
ribRefWatcher.watchDeletedObject(null, "")
verifyNoInteractions(referenceWatcher)
}

@Test
fun watchDeletedObject_whenReferenceWatcherIsNullULeakEnabled_shouldDoNothing() {
ribRefWatcher.enableULeakLifecycleTracking()
ribRefWatcher.watchDeletedObject(Any())
ribRefWatcher.watchDeletedObject(Any(), "")
verifyNoInteractions(referenceWatcher)
}

Expand All @@ -84,15 +84,15 @@ class RibRefWatcherTest {
ribRefWatcher.enableULeakLifecycleTracking()
val obj = Any()
ribRefWatcher.setReferenceWatcher(referenceWatcher)
ribRefWatcher.watchDeletedObject(obj)
verify(referenceWatcher).watch(obj)
ribRefWatcher.watchDeletedObject(obj, "")
verify(referenceWatcher).watch(obj, "")
}

@Test
fun watchDeletedObject_whenNonNullRefULeakDisabled_shouldDoNothing() {
val obj = Any()
ribRefWatcher.setReferenceWatcher(referenceWatcher)
ribRefWatcher.watchDeletedObject(obj)
verify(referenceWatcher, never()).watch(obj)
ribRefWatcher.watchDeletedObject(obj, "")
verify(referenceWatcher, never()).watch(obj, "")
}
}

0 comments on commit 04a7f08

Please sign in to comment.