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.

Solutions

  1. Remove the shutdown() call on the singleton — it lives for the process lifetime
  2. If you need lifecycle-scoped instances, build your own: DokitPicasso p = new DokitPicasso.Builder(context).build(); ... p.shutdown();
  3. 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

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


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/88cd13e0c5b90417. Report an issue: GitHub.

Appendix: 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)