Anton Holovin
Anton Holovin

Reputation: 5673

Glide freezes when trying to load image inside transform()

I need to transform image during loading by Glide.

Inside transform() method I also need to load another image and draw it over current image. So I also use Glide for load task inside transform(). I use blocking method get() because I need to get result instantly and transform() operates not in the main thread which is good.

It is very important to note that code loads several images at the same time and all images should be transformed, so all processes of transformation include loading of additional image by Glide.

My problem is when I use Glide to load image inside transform() method in parallel several times in a row - it blocks code and does not continue work.

Here is the code:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    for (int i = 0; i < 5; i++) {

        final int a = i;

        Glide.with(this)
                .fromResource()
                .asBitmap()
                .load(R.drawable.ic_non_provisioned)
                .transform(new BitmapTransformation(this) {
                    @Override
                    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {

                        Log.d(MainActivity.class.getName(), String.format("Before load. Thread %s",
                                Thread.currentThread().getName()));

                        Bitmap bitmap = load(); // freezes here

                        Log.d(MainActivity.class.getName(), "After load");


                        return toTransform;
                    }

                    @Override
                    public String getId() {
                        return String.valueOf(a); // important for demo of bug
                    }
                })
                .into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                        Log.d(MainActivity.class.getName(), "Load finished");
                    }
                });
        }
}

private Bitmap load() {
    try {
        return Glide.with(this)
                .fromResource()
                .asBitmap()
                .load(R.drawable.ic_search)
                .into(20, 20)
                .get(); // freezes here
    } catch (Exception e) {
        return null;
    }
}

What I see in console output:

D/com.golovin.androidtest.MainActivity: Before load. Thread fifo-pool-thread-0 D/com.golovin.androidtest.MainActivity: Before load. Thread fifo-pool-thread-1

That's all. Glide does not continue a work and I see nothing. By the way if I use get(2, TimeUnit.SECONDS) instead of get() Glide will throw TimeoutException.

What do I do wrong?

Upvotes: 0

Views: 3196

Answers (1)

TWiStErRob
TWiStErRob

Reputation: 46480

You're blocking Glide by requesting a new image. I guess that's obvious, here's why:

  • .into(new SimpleTarget<Bitmap>...) schedules 5 new loads: 1, 2, 3, 4, 5
  • 2 of them start in fifo-pool (you likely have two cores)
  • Bitmap is loaded and passed to your Transform
  • those two transforms call .into(20, 20) which schedules 2 more loads: 6, 7
  • .get() blocks while those two are completed

The problem is that for load 6 and 7 to complete Glide first has to complete the first 5 out of which 1 and 2 are in progress, but the two in-progress ones require the 6 and 7 to complete -> cycle.

Solution 1

You just simply cannot block Glide's threads with a Glide load, try to flip the order:

....load(R.drawable.ic_search).into(new SimpleTarget(20, 20) {
    @Override public void onResourceReady(Bitmap icon, GlideAnimation glideAnimation) {
        for (int i = 0; i < 5; i++) {
            // load others using icon passed into the transformation
        }
    }
});

Solution 2

You can fire up your own executor service and schedule background tasks that look like this:

Bitmap icon = Glide.....load(R.drawable.ic_search).into(20, 20).get();
Bitmap result = Glide.....load(R.drawable.ic_non_provisioned).transform(using icon).into(Target.SIZE_ORIGINAL).get();
return result; // deliver to the UI thread somehow to be set in a view

Solution 3

You can simply load both Bitmaps in the sync way and combine them within the task without a Glide transformation. You can access the pool from Glide.get(context).getBitmapPool() if you want to reuse some Bitmaps.

Solution 4

Keep in mind that it may not worth all this extra complexity, consider using a layout to position one ImageView on top of another (with a FrameLayout for example) and load as ususal.


Solution 1 is more convenient than Solution 2 and 3, because you already have the icon when you start the other loads and you're on the UI thread, so you can apply the transformation and Glide will deliver the images to the ImageView (if any).

Upvotes: 4

Related Questions