koral--/android-gif-drawable · error · IllegalArgumentException
Bitmap is recycled
Error message
Bitmap is recycled
What it means
checkBuffer validates the Bitmap passed to seekToTime/seekToFrame before native code renders into it. If the bitmap has been recycled, drawing into it would crash the native layer, so the library throws IllegalArgumentException early with a clear message.
Solutions
- Do not recycle the bitmap while the decoder may still use it; keep a strong reference until done seeking
- Check bitmap.isRecycled() before calling seekTo* and allocate a fresh ARGB_8888 bitmap if recycled
- Wrap seek calls in try-catch for IllegalArgumentException and recreate the buffer
Example fix
// before
decoder.seekToFrame(frameIndex, bitmap); // bitmap may be recycled
// after
if (bitmap == null || bitmap.isRecycled()) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
decoder.seekToFrame(frameIndex, bitmap); Defensive patterns
Strategy: validation
Validate before calling
if (buffer == null || buffer.isRecycled()) { buffer = Bitmap.createBitmap(decoder.getWidth(), decoder.getHeight(), Bitmap.Config.ARGB_8888); } Type guard
Bitmap ensureLiveBitmap(Bitmap b, int w, int h) { return (b == null || b.isRecycled() || b.getWidth() < w || b.getHeight() < h) ? Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) : b; } Try / catch
try { decoder.seekToFrame(i, buffer); } catch (IllegalArgumentException e) { buffer = Bitmap.createBitmap(decoder.getWidth(), decoder.getHeight(), Bitmap.Config.ARGB_8888); decoder.seekToFrame(i, buffer); } Prevention
- Never recycle a bitmap still referenced by an active GifDecoder
- Centralize buffer creation in one helper that checks isRecycled()
- Tie buffer lifetime to the decoder's lifecycle
When it happens
Trigger: Calling seekToTime(millis, bitmap) or seekToFrame(index, bitmap) on GifDecoder with a Bitmap on which recycle() was already called (e.g. after releasing previous resources, or bitmap evicted and recycled by your own cache).
Common situations: Reusing a bitmap field after an activity/fragment teardown recycled it; bitmap pools recycling on background threads while a seek is in flight; confusing GifDrawable's internal buffer with a user-supplied bitmap that was recycled elsewhere.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Sample size out of range <1, 65535>
- Bitmap ia too small, size must be greater than or equal to…
- Only Config.ARGB_8888 is supported. Current bitmap config
- Position is not positive
- Frame index is not positive
AI-assisted analysis of koral--/android-gif-drawable@26ff795f78 (2026-09-10).
Data as JSON: /api/errors/7a0841ce963cfef1.
Report an issue: GitHub.
Appendix: source
Thrown at android-gif-drawable/src/main/java/pl/droidsonroids/gif/GifDecoder.java:162
}
/**
* @return true if GIF is animated (has at least 2 frames and positive duration), false otherwise
*/
public boolean isAnimated() {
return mGifInfoHandle.getNumberOfFrames() > 1 && getDuration() > 0;
}
/**
* See {@link GifDrawable#recycle()}
*/
public void recycle() {
mGifInfoHandle.recycle();
}
private void checkBuffer(final Bitmap buffer) {
if (buffer.isRecycled()) {
throw new IllegalArgumentException("Bitmap is recycled");
}
if (buffer.getWidth() < mGifInfoHandle.getWidth() || buffer.getHeight() < mGifInfoHandle.getHeight()) {
throw new IllegalArgumentException("Bitmap ia too small, size must be greater than or equal to GIF size");
}
if (buffer.getConfig() != Bitmap.Config.ARGB_8888) {
throw new IllegalArgumentException("Only Config.ARGB_8888 is supported. Current bitmap config: " + buffer.getConfig());
}
}
}
View on GitHub (pinned to 26ff795f78)