{"record":{"id":"9687c85833ad224b","repo":"didi/DoKit","slug":"method-call-should-not-happen-from-the-main-thread","errorCode":null,"errorMessage":"Method call should not happen from the main thread.","messagePattern":"Method call should not 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":130,"sourceCode":"    } else {\n      result = bitmap.getRowBytes() * bitmap.getHeight();\n    }\n    if (result < 0) {\n      throw new IllegalStateException(\"Negative size: \" + bitmap);\n    }\n    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) {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/Utils.java#L112-L148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before (main thread)\nBitmap b = picasso.load(url).get(); // throws\n\n// after\nexecutor.execute(() -> {\n    Bitmap b = picasso.load(url).get();\n    new Handler(Looper.getMainLooper()).post(() -> imageView.setImageBitmap(b));\n});","handlingStrategy":"validation","validationCode":"if (Looper.myLooper() == Looper.getMainLooper()) {\n    throw new IllegalStateException(\"sync get() off main only\");\n}\nBitmap b = picasso.load(url).get();","typeGuard":"static boolean isOnMainThread() {\n    return Looper.getMainLooper().getThread() == Thread.currentThread();\n}","tryCatchPattern":"Not applicable — thread misuse must be fixed structurally, not caught.","preventionTips":["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."],"tags":["android","picasso","threading","main-thread","sync-io"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}