didi/DoKit · error · IllegalStateException

Singleton instance already exists.

Error message

Singleton instance already exists.

What it means

setSingletonInstance(DokitPicasso) installs the object returned by with(); it may be called at most once and only before the first with() call. A second call — or any call after with() already lazily created the default — throws IllegalStateException('Singleton instance already exists.'). This protects callers that captured with()'s instance from having it swapped underneath them.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/DokitPicasso.java:678

    if (singleton == null) {
      synchronized (DokitPicasso.class) {
        if (singleton == null) {
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }

  /**
   * Set the global instance returned from {@link #with}.
   * <p>
   * This method must be called before any calls to {@link #with} and may only be called once.
   */
  public static void setSingletonInstance(DokitPicasso picasso) {
    synchronized (DokitPicasso.class) {
      if (singleton != null) {
        throw new IllegalStateException("Singleton instance already exists.");
      }
      singleton = picasso;
    }
  }

  /** Fluent API for creating {@link DokitPicasso} instances. */
  @SuppressWarnings("UnusedDeclaration") // Public API.
  public static class Builder {
    private final Context context;
    private Downloader downloader;
    private ExecutorService service;
    private Cache cache;
    private Listener listener;
    private RequestTransformer transformer;
    private List<RequestHandler> requestHandlers;
    private Bitmap.Config defaultBitmapConfig;

    private boolean indicatorsEnabled;

View on GitHub (pinned to 626827cddb)

Solutions

  1. Call setSingletonInstance exactly once, in Application.onCreate(), before any image load
  2. Wrap test setup: only call it if the singleton is not yet set (guarded by a try/catch or your own initialized flag)
  3. Remove stray with() calls (e.g. in static initializers) that run before your setSingletonInstance
  4. In multi-test processes, prefer building and injecting Picasso instances directly instead of touching the singleton

Example fix

// before
public void onCreate() {
  super.onCreate();
  DokitPicasso.setSingletonInstance(defaultPicasso); // crashes on 2nd init
}

// after
private static boolean picassoInstalled;
public void onCreate() {
  super.onCreate();
  if (!picassoInstalled) {
    try {
      DokitPicasso.setSingletonInstance(defaultPicasso);
    } catch (IllegalStateException ignored) { /* already installed */ }
    picassoInstalled = true;
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Install once, before any with()/load call — typically in Application.onCreate:
public class App extends Application {
  @Override public void onCreate() {
    super.onCreate();
    synchronized (DokitPicasso.class) {
      try {
        DokitPicasso.setSingletonInstance(
            new DokitPicasso.Builder(this).build());
      } catch (IllegalStateException alreadyInstalled) {
        // second init in the same process (tests, relaunch) — safe to ignore
      }
    }
  }
}

Try / catch

try {
  DokitPicasso.setSingletonInstance(picasso);
} catch (IllegalStateException e) {
  if ("Singleton instance already exists.".equals(e.getMessage())) {
    Log.d(TAG, "Singleton already installed; reusing it");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling setSingletonInstance(picasso) in both Application.onCreate and a test setup; two libraries each installing their own singleton; calling with(context) anywhere first (initializes the default) and then setSingletonInstance.

Common situations: Robolectric/JUnit test suites reusing one process where app onCreate runs per test; crash-reporting or leak-canning code re-initializing on relaunch within the same process; migration code adding setSingletonInstance to an app that already called with() eagerly.

Related errors


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