bumptech / glide

An image loading and caching library for Android focused on smooth scrolling
https://bumptech.github.io/glide/
Other
34.67k stars 6.12k forks source link

Exception "Cannot obtain size for recycled Bitmap",support recycled bitmap in LruResourceCache #4524

Open runInFuture opened 3 years ago

runInFuture commented 3 years ago

Glide Version:4.11.0

Integration libraries:None

Device/Android Version:ALL

Issue details / Repro steps / Use case background

When a bitmap is loaded, then show in screen, it reasonablly to expected hit cache in next load of the same url.

So i set ActiveResourceRetentionAllowed to true in my app. But got some crash like:

IllegalStateException: Cannot obtain size for recycled Bitmap

It throws when a resource call #getSize

I found desc:

// The return value of getAllocationByteCount silently changes for recycled bitmaps from the // internal buffer size to row bytes * height. To avoid random inconsistencies in caches, we // instead assert here.

Resource can remember the size of bitmap to avoid this.

Why not LruResourceCache just abandon a resource when found the bitmap behind is recycled, but throw a exception to crashing app.

Analysis

Glide has two level memory cache: LruResourceCache and ActiveResources.

When load a bitmap by call #into(), resource will be hold in ActiveResources. ActiveResources is actually a weakReference map,if the code outside of glide doesn't hold a strong ref of Target(E.g call #into with a view, view will hold target by setTag),it will be call gc soon.

When activeResource been gc,the bitmap will gone,will not put into LruResourceCache,unless set ActiveResourceRetentionAllowed to true.

Code

// first load
Glide.with(context)
        .load(url)
        .into(new SimpleTarget<Drawable>() {
            @Override
            public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {

            }
        });

// some delay
// Thread.sleep(10000)

// second load. Same url, but can't hit memory cache
Glide.with(context)
        .load(url)
        .into(new SimpleTarget<Drawable>() {
            @Override
            public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {

            }
        });

Question

Why not LruResourceCache just abandon a resource when found the bitmap behind is recycled, but throw a exception to crashing app.

sjudd commented 3 years ago

If a Bitmap ends up recycled in Glide's pool, it means it was freed more than once. While we can work around some of the resulting errors, we cannot work around all of them reliably. The only effective way to fix this is to make sure you only free the Bitmap once.

See http://bumptech.github.io/glide/doc/resourcereuse.html#common-errors