{"record":{"id":"69c50b2e3904eacb","repo":"didi/DoKit","slug":"target-callback-must-not-recycle-bitmap","errorCode":null,"errorMessage":"Target callback must not recycle bitmap!","messagePattern":"Target callback must not recycle bitmap!","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/TargetAction.java","lineNumber":38,"sourceCode":"\nfinal class TargetAction extends Action<Target> {\n\n  TargetAction(DokitPicasso picasso, Target target, Request data, int memoryPolicy, int networkPolicy,\n               Drawable errorDrawable, String key, Object tag, int errorResId) {\n    super(picasso, target, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key, tag,\n        false);\n  }\n\n  @Override void complete(Bitmap result, DokitPicasso.LoadedFrom from) {\n    if (result == null) {\n      throw new AssertionError(\n          String.format(\"Attempted to complete action with no result!\\n%s\", this));\n    }\n    Target target = getTarget();\n    if (target != null) {\n      target.onBitmapLoaded(result, from);\n      if (result.isRecycled()) {\n        throw new IllegalStateException(\"Target callback must not recycle bitmap!\");\n      }\n    }\n  }\n\n  @Override void error() {\n    Target target = getTarget();\n    if (target != null) {\n      if (errorResId != 0) {\n        target.onBitmapFailed(picasso.context.getResources().getDrawable(errorResId));\n      } else {\n        target.onBitmapFailed(errorDrawable);\n      }\n    }\n  }\n}\n","sourceCodeStart":20,"sourceCodeEnd":54,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/TargetAction.java#L20-L54","documentation":"Thrown by TargetAction.complete(Bitmap, LoadedFrom) after it invokes target.onBitmapLoaded(result, from) and then finds result.isRecycled() is true. Picasso retains ownership of delivered bitmaps — they may still live in the memory cache and be drawn later — so a Target that recycles the bitmap inside onBitmapLoaded corrupts shared state. The library detects this immediately and throws IllegalStateException.","triggerScenarios":"Implementing Target.onBitmapLoaded to call bitmap.recycle() (directly or via a helper like a custom drawable's recycle, or transforming the bitmap and recycling the original); the check runs right after the callback returns.","commonSituations":"Legacy code from the pre-Picasso era where manual recycling was routine; calling bitmap.recycle() after setting it on a custom view 'to free memory'; recycling in onBitmapLoaded before pushing the bitmap through another pipeline.","solutions":["Remove every bitmap.recycle() call inside onBitmapLoaded; Picasso manages the bitmap lifecycle, including cache eviction.","If you mutate the bitmap, do it via Picasso's transform(RequestHandler.Transform) API — Picasso recycles the source of a transformation itself.","If you truly need exclusive ownership, load with .memoryPolicy(MemoryPolicy.NO_CACHE) and understand the consequences instead of recycling a cached bitmap."],"exampleFix":"// before\n@Override public void onBitmapLoaded(Bitmap b, Picasso.LoadedFrom from) {\n    imageView.setImageBitmap(b);\n    b.recycle(); // crashes: bitmap may be cached/shared\n}\n\n// after\n@Override public void onBitmapLoaded(Bitmap b, Picasso.LoadedFrom from) {\n    imageView.setImageBitmap(b); // Picasso owns lifecycle; do not recycle\n}","handlingStrategy":"validation","validationCode":"// Contract: never recycle inside onBitmapLoaded\n@Override public void onBitmapLoaded(Bitmap b, Picasso.LoadedFrom from) {\n    imageView.setImageBitmap(b);\n    // no b.recycle() anywhere in this callback\n}","typeGuard":null,"tryCatchPattern":"Catching this IllegalStateException is wrong — recycling already corrupted shared cache state. Prevent it by auditing every recycle() call in Target implementations.","preventionTips":["Picasso owns bitmap lifecycle; never call recycle() on delivered bitmaps.","Use Transform for bitmap mutation; Picasso recycles the source itself.","Grep for '.recycle()' in any class implementing Target."],"tags":["android","picasso","bitmap","memory","callback-contract"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}