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

  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.