didi/DoKit · error · IllegalStateException
Method call should happen from the main thread.
Error message
Method call should happen from the main thread.
What it means
Utils.checkMain() throws IllegalStateException('Method call should happen from the main thread.') when invoked off the UI thread. RequestCreator.into(ImageView, Callback) calls checkMain() first because it touches view state (placeholder setting, request tagging, deferred-fit measurement) that is only safe on the thread that owns the view — the main thread.
Source
Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/Utils.java:136
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) {
StringBuilder builder = new StringBuilder(prefix);
Action action = hunter.getAction();
if (action != null) {
builder.append(action.request.logId());
}
List<Action> actions = hunter.getActions();View on GitHub (pinned to 626827cddb)
Solutions
- Wrap the into() call in runOnUiThread(...) / Handler(Looper.getMainLooper()).post(...) from background callbacks.
- In coroutines use withContext(Dispatchers.Main) { picasso.load(url).into(iv) }; in RxJava observeOn(AndroidSchedulers.mainThread()).
- Better: issue into() on main at bind time and let Picasso do the async fetch itself — never chain your own network wait before into().
Example fix
// before (inside OkHttp callback — background thread)
call.enqueue(new Callback() {
public void onResponse(Call c, Response r) {
picasso.load(url).into(imageView); // throws
}
});
// after
runOnUiThread(() -> picasso.load(url).into(imageView)); Defensive patterns
Strategy: validation
Validate before calling
// Ensure main thread before view loads
if (Looper.myLooper() != Looper.getMainLooper()) {
new Handler(Looper.getMainLooper()).post(() -> picasso.load(url).into(iv));
} else {
picasso.load(url).into(iv);
} Type guard
static boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
} Try / catch
Not applicable — restructure so into() is always invoked from the UI thread.
Prevention
- Never call into(imageView) from network/Rx/executor callbacks directly.
- Wrap third-party callbacks with runOnUiThread or observeOn(mainThread()).
- Issue loads at bind time and let Picasso handle the async fetch.
When it happens
Trigger: Calling picasso.load(...).into(imageView) from a background thread: inside doInBackground of an AsyncTask, inside Retrofit/OkHttp callbacks (which run on a worker thread), inside Executor tasks, or in a coroutine without switching to Main.
Common situations: Loading an image after a network call completes and applying it straight from the response callback; doing image loads inside doInBackground; calling into() from a HandlerThread; using libraries whose callbacks default to background threads (RxJava observeOn(Schedulers.io()).subscribe { into(...) }).
Related errors
- Method call should not happen from the main thread.
- invalid shadow size
- Use setColor method of Paint class to specify line color. Do
- Use setStrokeWidth method of Paint class to specify line siz
- failed to get size
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/29dbec59b683b8c6.
Report an issue: GitHub.