Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug with the PostProcessor gives recycled bitmap #259

Closed
fernandocamargoai opened this issue Apr 22, 2013 · 8 comments
Closed

Bug with the PostProcessor gives recycled bitmap #259

fernandocamargoai opened this issue Apr 22, 2013 · 8 comments
Labels

Comments

@fernandocamargoai
Copy link

I have a ListView that display some custom views with images and these images can be repeated in the ListView.
What happens is the I want to apply some effects in this images before displaying them. Since the BitmapDisplayer runs in the UI Threads and the effects I'm applying can be slow, I'm putting these effects in a BitmapProcessor that is registered as postProcessor. The problem is that my BitmapProcessor is receiving recycled bitmaps and it gives me a exception when I try to write another bitmap with my effects applied.
I can't put it in the preProcessor because these same images is used with different effects in other activities.

This is my configuration:

defaultDisplayImageOptions = new DisplayImageOptions.Builder()
                .cacheInMemory()
                .cacheOnDisc()
                .showStubImage(R.drawable.stub)
                .build();

ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
                .taskExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
                .taskExecutorForCachedImages(AsyncTask.THREAD_POOL_EXECUTOR)
                .threadPoolSize(3)
                .threadPriority(Thread.NORM_PRIORITY - 2)
                .memoryCache(new LRULimitedMemoryCache(2 * 1024 * 1024))
                .discCache(new UnlimitedDiscCache(cacheDir))
                .discCacheSize(50 * 1024 * 1024)
                .discCacheFileNameGenerator(new HashCodeFileNameGenerator())
                .defaultDisplayImageOptions(defaultDisplayImageOptions)
                .build();

In each effect I'm going to apply, I create a new DisplayImageOptions like this:

capaOptions = new DisplayImageOptions.Builder()
        .cloneFrom(SoongzApp.getDefaultDisplayImageOptions())
        .postProcessor(capaBitmapProcessor)
        .build();

capaBackgroundOptions = new DisplayImageOptions.Builder()
        .cloneFrom(SoongzApp.getDefaultDisplayImageOptions())
        .postProcessor(capaBitmapProcessor)
        .build();

capaDesfocadaOptions = new DisplayImageOptions.Builder()
        .cloneFrom(SoongzApp.getDefaultDisplayImageOptions())
        .postProcessor(capaDesfocadaBitmapProcessor)
        .build();

One of the effects I apply is blurring the image (it happens in an Activity where there's only an image), other is applied with the creation of LayerDrawable and I transform it into a Bitmap in the PostProcessor.

I think I'm reaching the memory cache's limite and it's recycling the images. And, instead of recreating, it's passing the recycled image to my postProcessor.

@fernandocamargoai
Copy link
Author

Oh, I forgot to say. I'm using the version 1.8.4.

@nostra13
Copy link
Owner

First of all I DON'T RECOMMEND use do so:

.taskExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.taskExecutorForCachedImages(AsyncTask.THREAD_POOL_EXECUTOR)

It can cause UI lags.

At second: do you recycle Bitmap (Bitmap.recycle()) yourself anywhere?

@fernandocamargoai
Copy link
Author

No, I'm not recycling anywhere in my code. For a while, my workaround was
to put my code in the BitmapDisplayer with a AsyncTask, to create the
effects in background and set the drawable in the UI Thread.
This way, it's working good, but I wouldn't like to create another
AsyncTask.

Thank you for your recommendation, I'll remove that configuration.

@nostra13
Copy link
Owner

Show me please exception stacktrace from LogCat.

@fernandocamargoai
Copy link
Author

Here is the stacktrace:

04-22 16:25:12.916: ERROR/AndroidRuntime(26412): FATAL EXCEPTION: pool-2-thread-1
        java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@4200e1e8
        at android.graphics.Canvas.throwIfRecycled(Canvas.java:1026)
        at android.graphics.Canvas.drawBitmap(Canvas.java:1127)
        at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:393)
        at android.graphics.drawable.LayerDrawable.draw(LayerDrawable.java:345)
        at com.soongz.util.BitmapUtils.getBitmap(BitmapUtils.java:249)
        at com.soongz.ui.bitmap.CapaBitmapProcessor.process(CapaBitmapProcessor.java:22)
        at com.nostra13.universalimageloader.core.ProcessAndDisplayImageTask.run(ProcessAndDisplayImageTask.java:52)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
        at java.lang.Thread.run(Thread.java:856)

And here is the processor:

public class CapaBitmapProcessor implements BitmapProcessor {

    @Inject
    private CapaUtils capaUtils;
    @Inject
    private BitmapUtils bitmapUtils;

    @Override
    public Bitmap process(Bitmap bitmap) {
        // capaUtils.adicionarEfeitos(bitmap, false) creates a LayerDrawable
        return bitmapUtils.getBitmap(capaUtils.adicionarEfeitos(bitmap, false));
    }
}

    // Here is the code used to create the Bitmap
    public Bitmap getBitmap(Drawable drawable) {
        Bitmap capa = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(capa);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return capa;
    }

@nostra13
Copy link
Owner

It's interesting. Can you check if incoming Bitmap is recycled in post-processor? LIke this:

    @Override
    public Bitmap process(Bitmap bitmap) {
        if (bitmap.isRecycled()) {
                L.w("Incoming bitmap is recycled!");
        }
        // capaUtils.adicionarEfeitos(bitmap, false) creates a LayerDrawable
        return bitmapUtils.getBitmap(capaUtils.adicionarEfeitos(bitmap, false));
    }

I want to be sure UIL gives you recycled bitmap.

@fernandocamargoai
Copy link
Author

I changed my code to the following:

@Override
    public Bitmap process(Bitmap bitmap) {
        if (bitmap.isRecycled()) {
            Log.e("Soongz", "Bitmap recycled!");
        }
        return bitmapUtils.getBitmap(capaUtils.adicionarEfeitos(bitmap, false));
    }

And I see this in log one or more times:

04-25 11:43:04.639: ERROR/Soongz(2347): Bitmap recycled!

As I mentioned, it occurs in a ListView, when the same image is displayed in another item and I scroll to it.

@broglep-work
Copy link

We experience the same issue

nostra13 added a commit that referenced this issue Apr 29, 2013
Prevent recycling of cached in memory images.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants