Skip to content

Commit

Permalink
RefWatcher configuration using builder
Browse files Browse the repository at this point in the history
  • Loading branch information
pyricau committed Sep 28, 2016
1 parent eccba7a commit 111609e
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.squareup.leakcanary;

import android.app.Application;
import android.content.Context;
import java.util.concurrent.TimeUnit;

import static com.squareup.leakcanary.RefWatcher.DISABLED;
import static java.util.concurrent.TimeUnit.SECONDS;

/** A {@link RefWatcherBuilder} with appropriate Android defaults. */
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {

private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);

private final Context context;

AndroidRefWatcherBuilder(Context context) {
this.context = context.getApplicationContext();
}

/**
* Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
* overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*/
public AndroidRefWatcherBuilder listenerServiceClass(
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

/**
* Sets a custom delay for how long the {@link RefWatcher} should wait until it checks if a
* tracked object has been garbage collected. This overrides any call to {@link
* #watchExecutor(WatchExecutor)}.
*/
public AndroidRefWatcherBuilder watchDelay(long delay, TimeUnit unit) {
return watchExecutor(new AndroidWatchExecutor(unit.toMillis(delay)));
}

/**
* Sets the maximum number of heap dumps stored. This overrides any call to {@link
* #heapDumper(HeapDumper)} as well as any call to
* {@link LeakCanary#setDisplayLeakActivityDirectoryProvider(LeakDirectoryProvider)})}
*
* @throws IllegalArgumentException if maxStoredHeapDumps < 1.
*/
public AndroidRefWatcherBuilder maxStoredHeapDumps(int maxStoredHeapDumps) {
LeakDirectoryProvider leakDirectoryProvider =
new DefaultLeakDirectoryProvider(context, maxStoredHeapDumps);
LeakCanary.setDisplayLeakActivityDirectoryProvider(leakDirectoryProvider);
return heapDumper(new AndroidHeapDumper(context, leakDirectoryProvider));
}

/**
* Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
*/
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
}
return refWatcher;
}

@Override protected boolean isDisabled() {
return LeakCanary.isInAnalyzerProcess(context);
}

@Override protected HeapDumper defaultHeapDumper() {
LeakDirectoryProvider leakDirectoryProvider = new DefaultLeakDirectoryProvider(context);
return new AndroidHeapDumper(context, leakDirectoryProvider);
}

@Override protected DebuggerControl defaultDebuggerControl() {
return new AndroidDebuggerControl();
}

@Override protected HeapDump.Listener defaultHeapDumpListener() {
return new ServiceHeapDumpListener(context, DisplayLeakService.class);
}

@Override protected ExcludedRefs defaultExcludedRefs() {
return AndroidExcludedRefs.createAppDefaults().build();
}

@Override protected WatchExecutor defaultWatchExecutor() {
return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public final class AndroidWatchExecutor implements WatchExecutor {
private final long initialDelayMillis;
private final long maxBackoffFactor;

public AndroidWatchExecutor(int initialDelayMillis) {
public AndroidWatchExecutor(long initialDelayMillis) {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.os.Environment;
import com.squareup.leakcanary.internal.RequestStoragePermissionActivity;
import java.io.File;
Expand All @@ -40,19 +39,30 @@

public final class DefaultLeakDirectoryProvider implements LeakDirectoryProvider {

private static final int DEFAULT_MAX_STORED_HEAP_DUMPS = 7;

private static final String HPROF_SUFFIX = ".hprof";
private static final String PENDING_HEAPDUMP_SUFFIX = "_pending" + HPROF_SUFFIX;

/** 10 minutes */
private static final int ANALYSIS_MAX_DURATION_MS = 10 * 60 * 1000;

private final Context context;
private final int maxStoredHeapDumps;

private boolean writeExternalStorageGranted;
private boolean permissionNotificationDisplayed;
private volatile boolean writeExternalStorageGranted;
private volatile boolean permissionNotificationDisplayed;

public DefaultLeakDirectoryProvider(Context context) {
this(context, DEFAULT_MAX_STORED_HEAP_DUMPS);
}

public DefaultLeakDirectoryProvider(Context context, int maxStoredHeapDumps) {
if (maxStoredHeapDumps < 1) {
throw new IllegalArgumentException("maxStoredHeapDumps must be at least 1");
}
this.context = context.getApplicationContext();
this.maxStoredHeapDumps = maxStoredHeapDumps;
}

@Override public List<File> listFiles(FilenameFilter filter) {
Expand Down Expand Up @@ -176,9 +186,6 @@ private boolean directoryWritableAfterMkdirs(File directory) {
}

private void cleanupOldHeapDumps() {
Resources resources = context.getResources();
int configStoredHeapDumps = resources.getInteger(R.integer.leak_canary_max_stored_leaks);
int maxStoredHeapDumps = Math.max(configStoredHeapDumps, 1);
List<File> hprofFiles = listFiles(new FilenameFilter() {
@Override public boolean accept(File dir, String filename) {
return filename.endsWith(HPROF_SUFFIX);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import com.squareup.leakcanary.internal.DisplayLeakActivity;
Expand All @@ -38,47 +37,25 @@ public final class LeakCanary {
* references (on ICS+).
*/
public static RefWatcher install(Application application) {
return install(application, DisplayLeakService.class,
AndroidExcludedRefs.createAppDefaults().build());
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}

/**
* Creates a {@link RefWatcher} that reports results to the provided service, and starts watching
* activity references (on ICS+).
*/
public static RefWatcher install(Application application,
Class<? extends AbstractAnalysisResultService> listenerServiceClass,
ExcludedRefs excludedRefs) {
if (isInAnalyzerProcess(application)) {
return RefWatcher.DISABLED;
}
enableDisplayLeakActivity(application);
HeapDump.Listener heapDumpListener =
new ServiceHeapDumpListener(application, listenerServiceClass);
RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
return refWatcher;
}

/**
* Creates a {@link RefWatcher} with a default configuration suitable for Android.
*/
public static RefWatcher androidWatcher(Context context, HeapDump.Listener heapDumpListener,
ExcludedRefs excludedRefs) {
LeakDirectoryProvider leakDirectoryProvider = new DefaultLeakDirectoryProvider(context);
DebuggerControl debuggerControl = new AndroidDebuggerControl();
AndroidHeapDumper heapDumper = new AndroidHeapDumper(context, leakDirectoryProvider);
Resources resources = context.getResources();
int watchDelayMillis = resources.getInteger(R.integer.leak_canary_watch_delay_millis);
AndroidWatchExecutor executor = new AndroidWatchExecutor(watchDelayMillis);
return new RefWatcher(executor, debuggerControl, GcTrigger.DEFAULT, heapDumper,
heapDumpListener, excludedRefs);
/** Builder to create a customized {@link RefWatcher} with appropriate Android defaults. */
public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);
}

public static void enableDisplayLeakActivity(Context context) {
setEnabled(context, DisplayLeakActivity.class, true);
}

/**
* If you build a {@link RefWatcher} with a {@link AndroidHeapDumper} that has a custom {@link
* LeakDirectoryProvider}, then you should also call this method to make sure the activity in
* charge of displaying leaks can find those on the file system.
*/
public static void setDisplayLeakActivityDirectoryProvider(
LeakDirectoryProvider leakDirectoryProvider) {
DisplayLeakActivity.setLeakDirectoryProvider(leakDirectoryProvider);
Expand Down
20 changes: 0 additions & 20 deletions leakcanary-android/src/main/res/values/leak_canary_int.xml

This file was deleted.

2 changes: 0 additions & 2 deletions leakcanary-android/src/main/res/values/leak_canary_public.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,5 @@
<public name="leak_canary_display_activity_label" type="string"/>
<public name="leak_canary_heap_dump_toast" type="layout"/>
<public name="leak_canary_icon" type="drawable"/>
<public name="leak_canary_max_stored_leaks" type="integer"/>
<public name="leak_canary_watch_delay_millis" type="integer"/>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@

import static com.squareup.leakcanary.Preconditions.checkNotNull;

/** Data structure holding information about a heap dump. */
public final class HeapDump implements Serializable {

/** Receives a heap dump to analyze. */
public interface Listener {
Listener NONE = new Listener() {
@Override public void analyze(HeapDump heapDump) {
}
};

void analyze(HeapDump heapDump);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@

import java.io.File;

/** Dumps the heap into a file. */
public interface HeapDumper {
HeapDumper NONE = new HeapDumper() {
@Override public File dumpHeap() {
return RETRY_LATER;
}
};

File RETRY_LATER = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,7 @@
*/
public final class RefWatcher {

public static final RefWatcher DISABLED = new RefWatcher(new WatchExecutor() {
@Override public void execute(Retryable retryable) {
}
}, new DebuggerControl() {
@Override public boolean isDebuggerAttached() {
// Skips watching.
return true;
}
}, GcTrigger.DEFAULT, new HeapDumper() {
@Override public File dumpHeap() {
return RETRY_LATER;
}
}, new HeapDump.Listener() {
@Override public void analyze(HeapDump heapDump) {
}
}, new ExcludedRefs.BuilderWithParams().build());
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();

private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
Expand All @@ -61,9 +46,8 @@ public final class RefWatcher {
private final HeapDump.Listener heapdumpListener;
private final ExcludedRefs excludedRefs;

public RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl,
GcTrigger gcTrigger, HeapDumper heapDumper, HeapDump.Listener heapdumpListener,
ExcludedRefs excludedRefs) {
RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,
HeapDumper heapDumper, HeapDump.Listener heapdumpListener, ExcludedRefs excludedRefs) {
this.watchExecutor = checkNotNull(watchExecutor, "watchExecutor");
this.debuggerControl = checkNotNull(debuggerControl, "debuggerControl");
this.gcTrigger = checkNotNull(gcTrigger, "gcTrigger");
Expand Down Expand Up @@ -91,6 +75,9 @@ public void watch(Object watchedReference) {
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
Expand Down
Loading

0 comments on commit 111609e

Please sign in to comment.