Glide: Round just top corners of image ( Question)

Created on 1 May 2016  路  25Comments  路  Source: bumptech/glide

Hi there, I'm using Glide in my recyclerview adapter as you provided in sample app.
I want to round top corners of image, but now, I'm using this way that round all corners:

mGlide.load(post.Image.ImageLow.ImageUrl)
    .asBitmap()
    .skipMemoryCache(true)
    .diskCacheStrategy(DiskCacheStrategy.RESULT)
    .priority(Priority.IMMEDIATE)
    .override(calculateWidth(post), calculateHeight(post))
    .into(new BitmapImageViewTarget(holder.POST_IMAGE) {
        @Override protected void setResource(Bitmap resource) {
            RoundedBitmapDrawable roundImage = RoundedBitmapDrawableFactory.create(mActivity.getResources(), resource);
            roundImage.setCornerRadius(Utils.dpToPx(3, mActivity));
            holder.POST_IMAGE.setImageDrawable(roundImage);

        }
    });

Is there any default transformation to do that in Glide library?
I have a bad experience of using glide-transformations for this , this made my recyclerview so Laggy :(

question

All 25 comments

I think that support lib class doesn't support that.
Glide doesn't have anything built in except for the BitmapTransformation base class.
In there you can go wild as @wasabeef demonstrated.
If you don't want to use that lob you can roll your own, just extend the above mentioned class (see his lib or Glide's CenterCrop/FitCenter for example, and read the docs in the source code).

Also see http://stackoverflow.com/a/24737726/253468 for an algorithm.

Btw, I think you got laggy from using a transformation, not because you used that library. If your override calculates a too big size that can be a culprit, also make sure you're using the latest Glide; and remove that skipMemoryCache call and make sure the calculate* methods return consistent values to speed up scrolling: 1px difference in there can mean bad behaviour.

I'm using Glide 3.7.0
Let me show you, what I'm doing in my calculate method. I've a staggered recyclerview like pinterest app ( this is mine / As you can see all corners are rounded by this method ):

screenshot_2016-05-01-12-57-19

for determine and set width and height to ImageView before image load by glide, I calculate them by these information:

  1. I get Screen width and then divided it by two, then minus margins and padding from that to get each column width in adapter

java screenWidth = Utils.getScreenWidth((Activity) mActivity); minusWidth = Utils.dpToPx(12, mActivity); realWidth = (screenWidth / 2) - minusWidth;

  1. from API, I get width and height for each image:

java int ImageWidth = currentPost.Image.ImageLow.ImageWidth; int ImageHeight = currentPost.Image.ImageLow.ImageHeight;

  1. in calculate method, I calculate ImageView width and height to fit column, like this ( by keeping aspect-ration):

java int mNewWidth = (int) Math.ceil(realWidth); int mNewHeight = (ImageHeight*mNewWidth) / ImageWidth;

  1. in onBindViewHolder, I set ImageView height and width by calculated height and width:

java LayoutParams params = new LayoutParams(calculateWidth(currentPost), calculateHeight(currentPost)); holder.POST_IMAGE.setLayoutParams(params);

I. Is this strategy wrong?
II. why should remove skipMemoryCache?

I. Is this strategy wrong?

Not necessarily if you're happy with it, but there are ways to let the Android View system do the layout calculations for you and Glide would pick that value up. All you need is the aspect ratio of the source image (no hardcoded margins and getting screen size). See https://github.com/TWiStErRob/glide-support/blob/master/src/glide3/java/com/bumptech/glide/supportapp/github/_864_staggered_grid/FeedEntryViewHolder.java#L55

II. why should remove skipMemoryCache?

When you scroll down and then back up your images are being reloaded from the disk again and again, if you just scroll back and forth you shouldn't see the placeholder/empty space not even for a millisecond because memory cache loads in Glide are synchronous. Same applies for backing out an activity and opening it again. Newer devices have hundreds of megabytes available to JVMs which can be filled with these cached Bitmaps. Just out of curiosity: why did you put it there in the first place? (maybe you had a good reason I'm not aware of)


As for this issue of rounding only the top, I think you'll need to create your own transformation and even than you'll have problems because even Android's Canvas API doesn't have built-in facilities to draw different corners. You can, however, do something like: copy full Bitmap to ARGB8888, then clear a 3dp by 3dp square to transparent, then over-draw a 6dp by 6dp circle of the source image so you get rounded corners there; OR extend RoundedBitmapDrawable and override the draw method like this (to draw over the bottom rounded corners with the original image's pixels):

@Override public void draw(Canvas canvas) {
    super.draw(canvas);
    Bitmap bitmap = getBitmap();
    // corners of getBounds() with getCornerRadius() sides
    RectF bottomLeft = ...;
    RectF bottomRight = ...;
    // calculate based on the output rects and getPaint().getShader().getLocalMatrix()
    Rect bottomLeftBitmap, bottomRightBitmap = ...;
    canvas.drawBitmap(bitmap, bottomLeftBitmap, bottomLeft, null);
    canvas.drawBitmap(bitmap, bottomRightBitmap, bottomRight, null);
}

I just remembered that I have dealt with this drawable before, check out how you can use Glide's transcode facility to create it without a custom target:

.transcode(new RoundedDrawableTranscoder(context), RoundedBitmapDrawable.class)
.into(imageView)

Thank you for your prompt reply and, Thank you for your suggestions on how to make my app more efficient :+1:

Hi there again
After our last discussion, I changed my adapter base what you've said in this link and thanks, it's work more flexible. But some issue is there that annoying.

Base on your recommendation, I'm using @wasabeef, transformation library, but some starnge behavior occurred as I mention here

Now, more important thing to me is glide
this is what I'm using in my RecyclerView adapter:

glide.load(url)
    .priority(Priority.IMMEDIATE)
    .override(width, height)
    .diskCacheStrategy(DiskCacheStrategy.RESULT)
    .skipMemoryCache(true)
    .priority(Priority.IMMEDIATE)
    .bitmapTransform(new RoundedCornersTransformation(this.image.getContext(), AndroidUtilities.dpToPx(6, this.image.getContext()), 0, RoundedCornersTransformation.CornerType.TOP))
    .listener(new RequestListener<String, GlideDrawable>() {
        @Override public boolean onException(Exception error, String model, Target<GlideDrawable> target, boolean isFirstResource) {
            return false;
        }
        @Override public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
            return false;
        }               
    })
    .into(this.image);

Now my Questions:

  • As I remember you said to me to remove skipMemoryCache(true), But When I remove this Heap Memory rising very fast! and this issue worry me in particular. Is it Okay?
  • I think my images load slower in compare with other libraries like picasso. Is there any further configuration to get better result?
    this my Configuration:

``` java
public class GlideConfiguration implements GlideModule {
@Override public void applyOptions(Context context, GlideBuilder builder) {
// Prefer higher quality images unless we're on a low RAM device
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
builder.setDecodeFormat(ActivityManagerCompat.isLowRamDevice(activityManager) ? DecodeFormat.PREFER_RGB_565 : DecodeFormat.PREFER_ARGB_8888)
}

  @Override public void registerComponents(Context context, Glide glide) {}

}
```

  • Are have any idea about strange behavior of @wasabeef library?

But When I remove this Heap Memory rising very fast!

Memory cache should help with scrolling a lot, the resources from memory can be loaded sync, whereas without it Glide has to go to the disk every time. I always think unused memory is a waste, it's better to cache something there for possible future use. As long as you can't reach an OOM it should be fine. Memory cache is bounded, so it won't raise to infinity, you can lower the size; this is the default (check what the calculator returns, maybe it's too big for your device):

int memoryCacheSize = new MemorySizeCalculator(context).getMemoryCacheSize();
builder.setMemoryCache(new LruResourceCache(memoryCacheSize));

I think my images load slower in compare with other libraries

You should measure that first, but make sure you're doing the same thing: there're a lot of things happening here and transformations are not cheap. You can always make loads faster by loading a smaller image (halve the w/h in override). Also make sure that the disk cache is hit (size and transformation has to match) and Glide doesn't go to the network on every display.

Are have any idea about strange behavior of @wasabeef library?

I commented there.

I changed My GlideConfiguration to this:

    MemorySizeCalculator calculator = new MemorySizeCalculator(context);
    int defaultMemoryCacheSize      = calculator.getMemoryCacheSize();
    int defaultBitmapPoolSize       = calculator.getBitmapPoolSize();

    int customMemoryCacheSize       = (int) (0.5 * defaultMemoryCacheSize);
    int customBitmapPoolSize        = (int) (0.5 * defaultBitmapPoolSize);

    builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize));
    builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize));
  • I reduce default memory size to be sure for better performance. Did I do the right?
  • Slow image
    You know what, I'm convinced to remove Transformation after bad result and your mention "transformations are not cheap".
    I got three different quality of image from API ( Original, Low, thumb). Based on user setting and it's internet connection speed one of them load as image, When source is "thumb" this is fast enough! but in "Low" quality as source it does not seems fast ( in this quality all images have 500 pixel width and different height).

Another thing that annoying is that when user scroll down and then back to top, loaded images in list does not here and seems that load again. I solved this issue by assign w/h to .override() and that works as charm But with new strategy ( based on your sample code), Despite of overriding, this happened, How can deal with this in this strategy?

Yep, size calculation looks good if that's what you want as the value, though I don't see why you just not / 2 instead of casting :)

Did you copy layoutParams.height = 0 in fixLayout? That's a really important line for caching.

If you have multiple qualities you can play around with thumbnail: load(low).thumbnail(load(thumb)) this will result in two downloads per list item, but the user should feel that it's faster than when using only low. You can apply the same transformation and crossFade so it just "clears up" when the better quality is loaded.

Yeap, I copy fixLayout As you used in sample code.
I confused about .thumbnail() , can you provid a snippet of code or a sample/tutorial link

I wrote it like this:

    String lowImage = img.Url;
    String thumbImage = timg.Url;
    DrawableRequestBuilder<String > thumbnailRequest = glide.load(turl).dontAnimate();

    glide.load(url)
    .priority(Priority.IMMEDIATE)
    .override(width, height)
    .diskCacheStrategy(DiskCacheStrategy.RESULT)
    .crossFade()
    .thumbnail(thumbnailRequest)
    .into(this.image);
  • Now, it's look better, But I think user internet usage growing by this strategy, isn't it? :)
  • What's your idea about time gap to image fill ImageView when user back to top?

If the thumbs are small enough the internet shouldn't be bad. I think if a user is concerned about internet usage they shouldn't be using an image-heavy app with limited internet then.

Try to use a lower priority than immediate, because Glide assigns thumbnails one class higher priority because it assumes they'll finish really fast; otherwise the thumbnail may be queued _after_ the normal load which defeats its purpose.

When you scroll back and forth for 2-3 rows only do the images load immediately? Does this time gap happen only when scrolling really far away from the top?

If it happens for 2-3 rows as well, try https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling to diagnose why the cache is not hit.

If it's happens only when you go far from top, then the memory cache is too small. Check the calculator's code to see what you're actually halving.

  • Okay I set priority to HIGH and it's get better than before, thanks for your advise :)
    but when I set .crossFade() to glide request, image view get blink for a while!
  • Yeah, it happened when user goes far away from top and scroll back fast to top. You know what, I'm so scary about Memory usage, cause I've 5 Fragment on a main activity that managed by FragmanetStateViewPager and all of them contain a list like this, How can manage memory if I changed GlideConfiguration?

I think the blink is https://github.com/bumptech/glide/issues/943#issuecomment-180839666, you can use that custom target to set it for now until that bug is fixed.

Yeah, lists in vewpager is an interesting question, I have yet to figure that out, you may get around it by unbinding the lists that are not visible (Glide.clear in onViewRecycled and probably setAdapter(null)) or decreasing the offscreen page size of the viewpager.

Hi there,
I'm here to say something about filling gap when user scroll list so far.
Based on sample you provided, I've used PauseOnFling() OnScrollListener. When I removed this, everything is Okay! and there is no delay to filling of images.

But I love that strategy,I think that is so goof for performance stuff :)

So you're saying that PauseOnFling causes a performance issue?

Oh No, I said that was so good to getting better performance!

Another issue raised here :(
When I using notifyItemChanged Image blinking!
this is my code:

String  url     = img.Url;

String  turl    = timg.Url;
DrawableRequestBuilder<String > thumbnailRequest = glide.load(turl).skipMemoryCache(true).crossFade(0);

int     width   = img.Width;
int     height  = img.Height;
fixlayout(width, height);

/* Is it GIF? set Gif badge*/
String URI          = imgQ.ImageOriginal.Url;
String Extention    = URI.substring(URI.lastIndexOf("."));
if((".gif").equals(Extention)){
    image.setBadgeVisibility(true);
    image.setBadge("GIF", initialGifBadgeColor);
}

glide.load(url)
     .priority(Priority.HIGH)
     .override(width, height)
     .diskCacheStrategy(DiskCacheStrategy.SOURCE)
     .crossFade(500)
     .thumbnail(thumbnailRequest)
     .listener(new RequestListener<String, GlideDrawable>() {

        @Override
        public boolean onException(Exception error, String model, Target<GlideDrawable> target, boolean isFirstResource) {
            return false;
        }

        @Override
        public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {

            progress.setVisibility(View.GONE);

            /* Turning image from BW to Colorful animation*/
            /*if(img.isIMAGE_HAS_FADED_IN()){

                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){

                    ViewCompat.setHasTransientState(image, true);
                    final ObservableColorMatrix cm  = new ObservableColorMatrix();
                    ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
                    animation.addUpdateListener(new AnimatorUpdateListener() {

                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {

                            cm.setSaturation(animation.getAnimatedFraction());
                            if(image.getDrawable() != null){
                                image.getDrawable().setColorFilter(new ColorMatrixColorFilter(cm));
                            }
                        }

                    });
                    animation.setDuration(2000);
                    animation.addListener(new AnimatorListenerAdapter() {

                        public void onAnimationEnd(Animator animation) {
                            ViewCompat.setHasTransientState(image, false);
                        };

                    });
                    animation.start();
                    img.setIMAGE_HAS_FADED_IN(true);
                }

            }*/

            return false;
        }

    })
     .into(new GlideDrawableImageViewTarget(this.image){

         @Override
        public void setDrawable(Drawable drawable) {

             if(drawable instanceof TransitionDrawable || img.isIMAGE_HAS_FADED_IN()){
                 ((TransitionDrawable) drawable).setCrossFadeEnabled(false);
             }
             super.setDrawable(drawable);

        }

     });

But still Image blinking :(

You're using skipMemoryCache on the thumbnail... so every time a new item is bound Glide has to go to the disk which takes time, and since you have no placeholder defined Glide will setImageDrawable(null) for that period.

Tips:

  • You can use image.setColorFilter without any checks. No need to check for drawable.
  • You can defined cm within the AnimatorUpdateListener anon inner class, just outside the update method.
  • you don't have any transformation defined, nor centerCrop nor fitCenter because of the custom target. Maybe add a dontTransform() to make it explicit that it was intentional (if it was :).
  • checking isIMAGE_HAS_FADED_IN in custom target may be unsafe as it is only checked if the drawable is NOT a TransitionDrawable, but you cast it anyway; did you want to use &&?

Hi there,
I got OOM error in my app sometimes, specially on old devices

java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
                                                                     at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:224)
                                                                     at java.util.concurrent.FutureTask.get(FutureTask.java:83)
                                                                     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor.afterExecute(FifoPriorityThreadPoolExecutor.java:96)
                                                                     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1096)
                                                                     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
                                                                     at java.lang.Thread.run(Thread.java:1019)
                                                                     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)
                                                                  Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
                                                                     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
                                                                     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
                                                                     at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeStream(Downsampler.java:329)
                                                                     at com.bumptech.glide.load.resource.bitmap.Downsampler.downsampleWithSize(Downsampler.java:220)
                                                                     at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:153)
                                                                     at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:50)
                                                                     at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:19)
                                                                     at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:39)
                                                                     at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:20)
                                                                     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeBitmapWrapper(GifBitmapWrapperResourceDecoder.java:121)
                                                                     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeStream(GifBitmapWrapperResourceDecoder.java:94)
                                                                     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:71)
                                                                     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:61)
                                                                     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:22)
                                                                     at com.bumptech.glide.load.engine.DecodeJob.decodeFromSourceData(DecodeJob.java:190)
                                                                     at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:177)
                                                                     at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
                                                                     at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
                                                                     at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
                                                                     at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
                                                                     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:444)
                                                                     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
                                                                     at java.util.concurrent.FutureTask.run(FutureTask.java:138)
                                                                     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
                                                                     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)聽
                                                                     at java.lang.Thread.run(Thread.java:1019)聽
                                                                     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)聽

I'm passed RequestManager to Adapter and use that as we talk before at this thread, but I got this error an app crashed :(

How can handle this? :(

It's simple: the bitmap doesn't fit. There are probably too many things in memory, or that single item is too big. You probably need to load a smaller image, but before you do that make sure that every non-visible Glide image target is cleared. RequestManager alone won't necessarily help, for example if you just stack fragments on top of each other the resources are still not freed (see #1337).

I've added Glide.with(this).onDestroy(); to onDestroyView() all fragments. But When first fragment load and recyclerview fills by data this happened [ Emulator - android API 10 ]:

D/dalvikvm: GC_CONCURRENT freed 1696K, 48% free 4783K/9031K, external 5681K/5952K, paused 0ms+0ms
D/dalvikvm: GC_EXTERNAL_ALLOC freed 351K, 44% free 5095K/9031K, external 5928K/5952K, paused 11ms
I/dalvikvm: Could not find method com.bumptech.glide.load.resource.drawable.GlideDrawable.getAlpha, referenced from method com.bumptech.glide.request.target.SquaringDrawable.getAlpha
W/dalvikvm: VFY: unable to resolve virtual method 23607: Lcom/bumptech/glide/load/resource/drawable/GlideDrawable;.getAlpha ()I
D/dalvikvm: VFY: replacing opcode 0x6e at 0x0002
D/dalvikvm: VFY: dead code 0x0005-0006 in Lcom/bumptech/glide/request/target/SquaringDrawable;.getAlpha ()I
I/dalvikvm: Could not find method com.bumptech.glide.load.resource.drawable.GlideDrawable.getCallback, referenced from method com.bumptech.glide.request.target.SquaringDrawable.getCallback
W/dalvikvm: VFY: unable to resolve virtual method 23608: Lcom/bumptech/glide/load/resource/drawable/GlideDrawable;.getCallback ()Landroid/graphics/drawable/Drawable$Callback;
D/dalvikvm: VFY: replacing opcode 0x6e at 0x0002
D/dalvikvm: VFY: dead code 0x0005-0006 in Lcom/bumptech/glide/request/target/SquaringDrawable;.getCallback ()Landroid/graphics/drawable/Drawable$Callback;
D/dalvikvm: GC_EXTERNAL_ALLOC freed 13K, 44% free 5096K/9031K, external 6660K/6852K, paused 10ms
D/dalvikvm: GC_EXTERNAL_ALLOC freed <1K, 44% free 5096K/9031K, external 6660K/6852K, paused 6ms
D/dalvikvm: GC_FOR_MALLOC freed <1K, 44% free 5096K/9031K, external 6730K/8404K, paused 5ms
D/dalvikvm: GC_EXTERNAL_ALLOC freed <1K, 44% free 5096K/9031K, external 6730K/8404K, paused 7ms
I/dalvikvm-heap: Clamp target GC heap from 16.515MB to 16.000MB
D/dalvikvm: GC_FOR_MALLOC freed 0K, 44% free 5096K/9031K, external 7279K/8404K, paused 5ms
D/dalvikvm: GC_EXTERNAL_ALLOC freed 2K, 44% free 5097K/9031K, external 7279K/8404K, paused 7ms
E/dalvikvm-heap: 500000-byte external allocation too large for this process.
D/dalvikvm: GC_FOR_MALLOC freed <1K, 44% free 5097K/9031K, external 6730K/8404K, paused 5ms
E/GraphicsJNI: VM won't let us allocate 500000 bytes
D/skia: --- decoder->decode returned false
D/dalvikvm: GC_EXTERNAL_ALLOC freed 1K, 44% free 5097K/9031K, external 6730K/8404K, paused 6ms
I/dalvikvm-heap: Clamp target GC heap from 16.516MB to 16.000MB
D/dalvikvm: GC_FOR_MALLOC freed 0K, 44% free 5097K/9031K, external 7279K/8404K, paused 5ms
E/PriorityExecutor: Request threw uncaught throwable
 java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
     at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:224)
     at java.util.concurrent.FutureTask.get(FutureTask.java:83)
     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor.afterExecute(FifoPriorityThreadPoolExecutor.java:96)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1096)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
     at java.lang.Thread.run(Thread.java:1019)
     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)
  Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
     at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeStream(Downsampler.java:329)
     at com.bumptech.glide.load.resource.bitmap.Downsampler.downsampleWithSize(Downsampler.java:220)
     at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:153)
     at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:50)
     at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:19)
     at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:39)
     at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:20)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeBitmapWrapper(GifBitmapWrapperResourceDecoder.java:121)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeStream(GifBitmapWrapperResourceDecoder.java:94)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:71)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:61)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:22)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperStreamResourceDecoder.decode(GifBitmapWrapperStreamResourceDecoder.java:24)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperStreamResourceDecoder.decode(GifBitmapWrapperStreamResourceDecoder.java:14)
     at com.bumptech.glide.load.resource.file.FileToStreamDecoder.decode(FileToStreamDecoder.java:39)
     at com.bumptech.glide.load.resource.file.FileToStreamDecoder.decode(FileToStreamDecoder.java:17)
     at com.bumptech.glide.load.engine.DecodeJob.loadFromCache(DecodeJob.java:222)
     at com.bumptech.glide.load.engine.DecodeJob.decodeSourceFromCache(DecodeJob.java:109)
     at com.bumptech.glide.load.engine.EngineRunnable.decodeFromCache(EngineRunnable.java:116)
     at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:99)
     at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:444)
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
     at java.util.concurrent.FutureTask.run(FutureTask.java:138)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)聽
     at java.lang.Thread.run(Thread.java:1019)聽
     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)聽
D/dalvikvm: GC_EXTERNAL_ALLOC freed 77K, 44% free 5108K/9031K, external 7271K/8404K, paused 17ms
E/dalvikvm-heap: 622000-byte external allocation too large for this process.
I/dalvikvm-heap: Clamp target GC heap from 16.519MB to 16.000MB
D/dalvikvm: GC_FOR_MALLOC freed <1K, 44% free 5108K/9031K, external 7271K/8404K, paused 6ms
E/GraphicsJNI: VM won't let us allocate 622000 bytes
D/skia: --- decoder->decode returned false
E/PriorityExecutor: Request threw uncaught throwable
 java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
     at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:224)
     at java.util.concurrent.FutureTask.get(FutureTask.java:83)
     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor.afterExecute(FifoPriorityThreadPoolExecutor.java:96)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1096)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
     at java.lang.Thread.run(Thread.java:1019)
     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)
  Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
     at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeStream(Downsampler.java:329)
     at com.bumptech.glide.load.resource.bitmap.Downsampler.downsampleWithSize(Downsampler.java:220)
     at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:153)
     at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:50)
     at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:19)
     at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:39)
     at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:20)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeBitmapWrapper(GifBitmapWrapperResourceDecoder.java:121)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeStream(GifBitmapWrapperResourceDecoder.java:94)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:71)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:61)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:22)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperStreamResourceDecoder.decode(GifBitmapWrapperStreamResourceDecoder.java:24)
     at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperStreamResourceDecoder.decode(GifBitmapWrapperStreamResourceDecoder.java:14)
     at com.bumptech.glide.load.resource.file.FileToStreamDecoder.decode(FileToStreamDecoder.java:39)
     at com.bumptech.glide.load.resource.file.FileToStreamDecoder.decode(FileToStreamDecoder.java:17)
     at com.bumptech.glide.load.engine.DecodeJob.loadFromCache(DecodeJob.java:222)
     at com.bumptech.glide.load.engine.DecodeJob.decodeSourceFromCache(DecodeJob.java:109)
     at com.bumptech.glide.load.engine.EngineRunnable.decodeFromCache(EngineRunnable.java:116)
     at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:99)
     at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:444)
     at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
     at java.util.concurrent.FutureTask.run(FutureTask.java:138)
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)聽
     at java.lang.Thread.run(Thread.java:1019)聽
     at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)聽
D/dalvikvm: GC_EXTERNAL_ALLOC freed 77K, 44% free 5118K/9031K, external 7271K/8404K, paused 13ms
I/dalvikvm-heap: Clamp target GC heap from 16.533MB to 16.000MB
D/dalvikvm: GC_FOR_MALLOC freed <1K, 44% free 5118K/9031K, external 7275K/8404K, paused 5ms
D/dalvikvm: GC_EXTERNAL_ALLOC freed 1K, 44% free 5118K/9031K, external 7275K/8404K, paused 14ms
I/dalvikvm-heap: Clamp target GC heap from 16.538MB to 16.000MB
D/dalvikvm: GC_FOR_MALLOC freed <1K, 44% free 5118K/9031K, external 7279K/8404K, paused 5ms
D/FirebaseInstanceId: background sync failed: MISSING_INSTANCEID_SERVICE, retry in 20s
D/FirebaseInstanceId: background sync failed: MISSING_INSTANCEID_SERVICE, retry in 40s

So it's not a big image (500k). What I don't understand is the 44% free... if that much is available why OOM? You need take a heap dump and look for leaks. It's very likely it shouldn't be this full by default. Also make sure that your calculations for pool/cache don't exceed the available memory.

I'm not familiar with head dump image analyzing :cry:
Can you please guide me :sob:
Give me some references please, thank you

Hi there,
I need your help again man
I'm using your sample code for showing image loading progress.
But something happen here, When i'm scrolling and new item add to recyclerview, glide try to load all previous images in adapter! How can remove requests from queue of RequestManager?!

I've read your discussion under this issue topic and you said:

Glide.clear(View) only works if you passed an object extending ViewTarget to into(...)

and finally do it in recyclerview:

@Override public void onViewDetachedFromWindow(ViewHolder holder) {
    Glide.clear(holder.contentsTarget);
    holder.contentsTarget = null;
}

Now I've target in my viewholder, but I get NPE when assign null to target in viewholder at onViewDetachedFromWindow

How can remove those request from glide RequestManage?

You should start opening new issues, don't abuse this question as a personal support thread. ;)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ersen-osman picture ersen-osman  路  3Comments

kenneth2008 picture kenneth2008  路  3Comments

piedpiperlol picture piedpiperlol  路  3Comments

sant527 picture sant527  路  3Comments

Ncit picture Ncit  路  3Comments