didi/DoKit · error · UnsupportedOperationException
buildAndInstall() should only be called once.
Error message
buildAndInstall() should only be called once.
What it means
LeakCanary's AndroidRefWatcherBuilder.buildAndInstall() registers a process-wide singleton RefWatcher in LeakCanaryInternals.installedRefWatcher and starts activity/fragment watchers. It deliberately throws UnsupportedOperationException on a second call to prevent duplicated watchers, duplicate heap analysis, and duplicated notifications.
Source
Thrown at Android/dokit-leakcanary/src/main/java/com/squareup/leakcanary/AndroidRefWatcherBuilder.java:96
AndroidRefWatcherBuilder maxStoredHeapDumps(int maxStoredHeapDumps) {
LeakDirectoryProvider leakDirectoryProvider =
new DefaultLeakDirectoryProvider(context, maxStoredHeapDumps);
LeakCanary.setLeakDirectoryProvider(leakDirectoryProvider);
return self();
}
/**
* Creates a {@link RefWatcher} instance and makes it available through {@link
* LeakCanary#installedRefWatcher()}.
*
* Also starts watching activity references if {@link #watchActivities(boolean)} was set to true.
*
* @throws UnsupportedOperationException if called more than once per Android process.
*/
public @NonNull
RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != RefWatcher.DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
@OverrideView on GitHub (pinned to 626827cddb)
Solutions
- Initialize LeakCanary in exactly one place, typically Application.onCreate()
- Guard the install: if (LeakCanary.isInAnalyzerProcess(this)) return; before calling buildAndInstall in multi-process apps
- Before a second install attempt (tests, plugins), check LeakCanary.installedRefWatcher != null or refactor to a single owner of initialization
Example fix
// before
LeakCanary.refWatcher(this).buildAndInstall(); // called in Application AND in a provider
// after
// single init point in Application.onCreate()
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
if (LeakCanary.installedRefWatcher() == null) {
LeakCanary.refWatcher(this).buildAndInstall();
} Defensive patterns
Strategy: validation
Validate before calling
if (LeakCanary.isInAnalyzerProcess(context)) return;
if (LeakCanary.installedRefWatcher() == null) { LeakCanary.refWatcher(context).buildAndInstall(); } Prevention
- Initialize LeakCanary in exactly one place (Application.onCreate)
- Audit SDKs (e.g. DoraemonKit modules) that may also call buildAndInstall
When it happens
Trigger: Calling LeakCanary.refWatcher(context).buildAndInstall() (or a custom builder chain) more than once in the same process: once in Application.onCreate and again in a debug initializer, a test setup, a library module, or after a manual install in a multi-dex/provider startup path.
Common situations: App + SDK both initializing LeakCanary (DoraemonKit's dokit-leakcanary module plus app-level init). Adding LeakCanary setup to a ContentProvider that runs before Application.onCreate where it is also installed. Tests that call buildAndInstall in setUp without resetting LeakCanaryInternals.installedRefWatcher.
Related errors
- Cannot set the LeakDirectoryProvider after it has already be
- leakTraceAsFakeException() can only be called when leakFound
- maxStoredHeapDumps must be at least 1
- Could not find char array in {}
- File does not exist: {}
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/471c0bd09a818d70.
Report an issue: GitHub.