didi/DoKit · error · IllegalStateException
Method call should not happen from the main thread.
Error message
Method call should not happen from the main thread.
What it means
Utils.checkNotMain() throws IllegalStateException('Method call should not happen from the main thread.') when invoked on the UI thread. Picasso uses it to enforce that blocking operations — the synchronous get() and the fetch-with-callback variants' internals — run on a worker thread, because performing network I/O on the main thread freezes the UI and, since Android 3.0, also triggers NetworkOnMainThreadException.
Source
Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/Utils.java:130
} else {
result = bitmap.getRowBytes() * bitmap.getHeight();
}
if (result < 0) {
throw new IllegalStateException("Negative size: " + bitmap);
}
return result;
}
static <T> T checkNotNull(T value, String message) {
if (value == null) {
throw new NullPointerException(message);
}
return value;
}
static void checkNotMain() {
if (isMain()) {
throw new IllegalStateException("Method call should not happen from the main thread.");
}
}
static void checkMain() {
if (!isMain()) {
throw new IllegalStateException("Method call should happen from the main thread.");
}
}
static boolean isMain() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
static String getLogIdsForHunter(BitmapHunter hunter) {
return getLogIdsForHunter(hunter, "");
}
static String getLogIdsForHunter(BitmapHunter hunter, String prefix) {View on GitHub (pinned to 626827cddb)
Solutions
- For async loading into a view, use into(imageView) or into(target) — these must run on main and handle threading internally.
- For a synchronous get(), move the call into a background executor or coroutine with Dispatchers.IO, then hop back to main to apply the bitmap.
- Use fetch() with a Callback for warm-up without a target (fetch itself enqueues async work).
Example fix
// before (main thread)
Bitmap b = picasso.load(url).get(); // throws
// after
executor.execute(() -> {
Bitmap b = picasso.load(url).get();
new Handler(Looper.getMainLooper()).post(() -> imageView.setImageBitmap(b));
}); Defensive patterns
Strategy: validation
Validate before calling
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new IllegalStateException("sync get() off main only");
}
Bitmap b = picasso.load(url).get(); Type guard
static boolean isOnMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
} Try / catch
Not applicable — thread misuse must be fixed structurally, not caught.
Prevention
- Use into()/fetch() for UI flows; reserve get() for worker threads.
- In coroutines call get() inside withContext(Dispatchers.IO).
- StrictMode will also flag main-thread I/O during development.
When it happens
Trigger: Calling picasso.load(url).get() (synchronous bitmap fetch) directly in an Activity/Fragment callback, button handler, or any code running on Looper.getMainLooper()'s thread.
Common situations: Prototyping a load inside onClick(); migrating async callback code into the UI layer; calling get() inside RecyclerView.onBindViewHolder; calling Picasso methods from a Handler posted to the main looper.
Related errors
- Method call should happen from the main thread.
- Unrecognized type of request: " + request
- Failed to decode stream.
- Transformation " + transformation.key() + " crashed with exc
- Transformation " + transformation.key() + " returned null af
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/9687c85833ad224b.
Report an issue: GitHub.