didi/DoKit · error · UnsupportedOperationException
Default singleton instance cannot be shutdown.
Error message
Default singleton instance cannot be shutdown.
What it means
shutdown() releases the cache, dispatcher, executor and stats of a Picasso instance. The default singleton (from DokitPicasso.with(context)) is shared process-wide, so shutting it down would break every subsequent load; Picasso guards it with UnsupportedOperationException('Default singleton instance cannot be shutdown.'). Only instances you built yourself via new DokitPicasso.Builder(...).build() may be shut down.
Source
Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/DokitPicasso.java:435
/** {@code true} if debug logging is enabled. */
public boolean isLoggingEnabled() {
return loggingEnabled;
}
/**
* Creates a {@link StatsSnapshot} of the current stats for this instance.
* <p>
* <b>NOTE:</b> The snapshot may not always be completely up-to-date if requests are still in
* progress.
*/
@SuppressWarnings("UnusedDeclaration") public StatsSnapshot getSnapshot() {
return stats.createSnapshot();
}
/** Stops this instance from accepting further requests. */
public void shutdown() {
if (this == singleton) {
throw new UnsupportedOperationException("Default singleton instance cannot be shutdown.");
}
if (shutdown) {
return;
}
cache.clear();
cleanupThread.shutdown();
stats.shutdown();
dispatcher.shutdown();
for (DeferredRequestCreator deferredRequestCreator : targetToDeferredRequestCreator.values()) {
deferredRequestCreator.cancel();
}
targetToDeferredRequestCreator.clear();
shutdown = true;
}
List<RequestHandler> getRequestHandlers() {
return requestHandlers;
}View on GitHub (pinned to 626827cddb)
Solutions
- Remove the shutdown() call on the singleton — it lives for the process lifetime
- If you need lifecycle-scoped instances, build your own: DokitPicasso p = new DokitPicasso.Builder(context).build(); ... p.shutdown();
- In tests, use setSingletonInstance(null-equivalent) is not supported — instead build fresh instances per test rather than shutting the singleton down
Example fix
// before
@Override protected void onDestroy() {
super.onDestroy();
DokitPicasso.with(this).shutdown(); // UnsupportedOperationException
}
// after
private final DokitPicasso picasso = new DokitPicasso.Builder(this).build();
@Override protected void onDestroy() {
super.onDestroy();
picasso.shutdown(); // OK: instance you own
} Defensive patterns
Strategy: validation
Validate before calling
// Only shutdown instances you constructed: final DokitPicasso owned = new DokitPicasso.Builder(context).build(); // ... later, when truly done: owned.shutdown(); // NEVER: DokitPicasso.with(context).shutdown();
Try / catch
if (picasso != null && picasso != DokitPicasso.with(context)) {
try { picasso.shutdown(); }
catch (UnsupportedOperationException e) { /* singleton — leave it alive */ }
} Prevention
- Remember with() returns a process-wide singleton — it must never be shut down
- In tests, build local instances per test instead of shutting the singleton
- Tag owned instances (field/local) vs shared (with()) in code review
When it happens
Trigger: Calling DokitPicasso.with(context).shutdown(), typically in onDestroy/onTerminate/test teardown; holding a reference obtained from with() and treating it as owned.
Common situations: Robolectric/unit tests tearing down the singleton between test cases; library code that builds its own Picasso but accidentally calls shutdown on with()'s instance; memory-leak 'fixes' that try to release the cache on background.
Related errors
- Picasso instance already shut down. Cannot submit new reques
- Singleton instance already exists.
- buildAndInstall() should only be called once.
- Unrecognized type of request: " + request
- Failed to decode stream.
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/88cd13e0c5b90417.
Report an issue: GitHub.