{"record":{"id":"29dbec59b683b8c6","repo":"didi/DoKit","slug":"method-call-should-happen-from-the-main-thread","errorCode":null,"errorMessage":"Method call should happen from the main thread.","messagePattern":"Method call should happen from the main thread\\.","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/Utils.java","lineNumber":136,"sourceCode":"    return result;\n  }\n\n  static <T> T checkNotNull(T value, String message) {\n    if (value == null) {\n      throw new NullPointerException(message);\n    }\n    return value;\n  }\n\n  static void checkNotMain() {\n    if (isMain()) {\n      throw new IllegalStateException(\"Method call should not happen from the main thread.\");\n    }\n  }\n\n  static void checkMain() {\n    if (!isMain()) {\n      throw new IllegalStateException(\"Method call should happen from the main thread.\");\n    }\n  }\n\n  static boolean isMain() {\n    return Looper.getMainLooper().getThread() == Thread.currentThread();\n  }\n\n  static String getLogIdsForHunter(BitmapHunter hunter) {\n    return getLogIdsForHunter(hunter, \"\");\n  }\n\n  static String getLogIdsForHunter(BitmapHunter hunter, String prefix) {\n    StringBuilder builder = new StringBuilder(prefix);\n    Action action = hunter.getAction();\n    if (action != null) {\n      builder.append(action.request.logId());\n    }\n    List<Action> actions = hunter.getActions();","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/Utils.java#L118-L154","documentation":"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.","triggerScenarios":"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.","commonSituations":"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(...) }).","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()."],"exampleFix":"// before (inside OkHttp callback — background thread)\ncall.enqueue(new Callback() {\n    public void onResponse(Call c, Response r) {\n        picasso.load(url).into(imageView); // throws\n    }\n});\n\n// after\nrunOnUiThread(() -> picasso.load(url).into(imageView));","handlingStrategy":"validation","validationCode":"// Ensure main thread before view loads\nif (Looper.myLooper() != Looper.getMainLooper()) {\n    new Handler(Looper.getMainLooper()).post(() -> picasso.load(url).into(iv));\n} else {\n    picasso.load(url).into(iv);\n}","typeGuard":"static boolean isMainThread() {\n    return Looper.getMainLooper().getThread() == Thread.currentThread();\n}","tryCatchPattern":"Not applicable — restructure so into() is always invoked from the UI thread.","preventionTips":["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."],"tags":["android","picasso","threading","main-thread","ui"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}