Glide Version:3.6.1
Integration libraries:no
Device/Android Version:Fails on all devices
So I am using Glide to load a web url .into imageviews in a gridview successfully. In each of these grid elements, I have an onclick listener through which I want to pass the Glide bitmap to a new activity. I know I can aquire the bitmap from glide with a callback, but for memory reasons (there are a lot of images) I do not want essentially two copies of all the images in memory.
So I am trying to only load the one that actually needs to be passed by accessing it in the onClickListener of the gridview element:
gridElement.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
object.setBitmapt(((BitmapDrawable)cover.getDrawable()).);
...
}
}
However, I get an exception java.lang.ClassCastException: com.bumptech.glide.request.target.SquaringDrawable cannot be cast to android.graphics.drawable.BitmapDrawable, presumably because Glide has its own object in the ImageView. I cannot find any documentation on the SquaringDrawable.
tl;dr: How can I get a bitmap from an ImageView that Glide was used on in the far future, after Glide has done it's thing?
ImageView.getDrawable() is not a safe operation, especially not in your use case. If you hold on to a Bitmap and for whatever reason it is needed to clear the ImageView the Bitmap would be reused or even recycled, essentially becoming unusable from your point of view.
Anything delivered to a target (in your case ImageView) is only valid between onResourceReady and onLoadCleared (see Target and descendants). Even if you capture it via a listener this may be true.(Btw, having two references to a Bitmap doesn't duplicate the memory, they just both point to the same memory.)
If you want to display the same Bitmap in another view Glide.load it again the same way you did earlier. This should hit the memory cache and give you the same Bitmap. If you share your grid's Glide load line, layout and what you want to do with the Bitmap, I can be more specific.
If you persist to go your way the Drawable that you get by default most of the time is GlideBitmapDrawable which may or may not be wrapped in squaring, so you can acquire the Bitmap via:
((GlideBitmapDrawable)view.getDrawable().getCurrent()).getBitmap()
Or if you add .asBitmap() to the load you can be sure getDrawable() returns a BitmapDrawable. Note that otherwise sometimes even the above line would fail: if the source image is an animated GIF. This is undocumented because you needn't know about it, what you can be sure that it's a GlideDrawable, just check the generics of .listener() or the return value of .into(). It gives Glide freedom to make internal changes and users a signal that there's something fishy going on when they encounter exception.
Basically, I do not require the bitmap just to pass it to the child activity. In the child activity, I blur the said image and then display it, a process which as far as I know can only be accomplished with a bitmap. So when I do a glide.load in the child activity I need to have a different call which returns the bitmap:
Glide
.with(this)
.load(a.getAlbum_artwork())
.asBitmap()
.into(new SimpleTarget<Bitmap>(300,300) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
setBackgroundImage(resource);
}
});
And from my testing, since this call is different than my first one, which is:
Glide.with(this)
.load(al.getAlbum_artwork())
.crossFade()
.error(R.drawable.missing_album_art)
.into(cover);
, it doesn't hit the cache.
Seems like there is not a clean solution. I do now understand why that cast is unsafe. Can I force Glide to take the calls from its cache?
So basically you display the thumbnail as the background, blurred. That modifies the original Bitmap or copies it as far as I know. This means that stealing the image from the grid ImageView would either modify it, which likely would corrupt the grid if the user goes back, or make a copy of it. asBitmap() is a key step here, but sadly it doesn't easily support crossFade (yet). To hit the memory cache you need the same decoders and stuff and asBitmap/default differ too much.
You have a few options (sorry if it's confusing, this is advanced Glide, and it's a little late for me):
I'm gonna assume throughout that setBackgroundImage(Bitmap) does view.setBackground(new BitmapDrawable(blur(bitmapParam)))).
I'm not sure what the interaction would be with the bitmap displayed on the grid if you modify the memcached bitmap in the activity, and do not copy it during blur.
Below are 3 distinct options, they're different ways to go. None of them are tested.
Take a look at the wiki to find out how to check if the cache is hit or not, for sure. Compare the keys in logs to see why it's not hitting.
``` diff
// grid
Glide.with(this)
.load(al.getAlbum_artwork())
+ .asBitmap()
- .crossFade()
+ // animation tricks from #840 if needed
.error(R.drawable.missing_album_art)
+ .override(300, 300) // size must match to hit memory cache
.into(cover);
// activity
cover view in XMLthis way the memory cache will hit.
``` diff
// activity
Glide
.with(this)
.load(a.getAlbum_artwork())
- .asBitmap()
+ .diskCacheStrategy(ALL) // re-use
+ .transform(new BlurTransformation(context)) // blur it in the background, not on UI thread
* .into(new SimpleTarget
* @Override public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
* view.setBackground(resource); // already blurred, and drawable
}
});
// grid
Glide.with(this)
.load(al.getAlbum_artwork())
.crossFade()
this would cache the blurred image onto the disk, and next time if the user opens the same album, it'll display fast (not from memory cache, just disk cache); this can be sped up if you want to have the illusion of very fast by doing a .preload(300, 300) before you startActivity, so Glide can do it's thing at the same time the layout and window creation happens.
You said you're loading network image, so since you would be displaying the same image loaded in different ways, it's worth caching the original network stream onto the disk as well (default strategy is RESULT).
If you don't have BlurTransformation it's easy to wrap your existing blurring code in a BitmapTransformation or look around wasabeef's.
Based on https://gist.github.com/TWiStErRob/0b0b083225f2869b5cb1
(you can find missing classes there)
```diff
// grid
Glide.with(this)
.load(al.getAlbum_artwork())
+ .asBitmap()
- .crossFade()
+ // animation tricks from #840 if needed
.error(R.drawable.missing_album_art)
// .override(300, 300) // no need to fix it
.into(cover);
public void onClick(View v) {
// pass size on to activity to be able to hit cache
intent.putExtra(cover.getDrawable().getIntrinsicWidth())
intent.putExtra(cover.getDrawable().getIntrinsicHeight())
}
// activity
// |memcache one|, |blurred one|
final GenericRequestBuilder
.with(this)
.using(new PassthroughModelLoader
.from(Bitmap.class)
.as(Bitmap.class)
.decoder(new BitmapBitmapResourceDecoder(context))
.cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context)))
.encoder(new BitmapEncoder())
.transform(new BlurTransformation(context)) // blur it in the background, not on UI thread
;
Glide
.with(this)
.load(a.getAlbum_artwork())
.asBitmap()
.centerCrop() // or whatever transformation you use on the cover view in XML
.override(intent.getExtra(w), intent.getExtra(h)) // this and above should hit memory cache
.into(new SimpleTarget
@Override public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
final Target> that = this;
blurringGlide
.load(resource) // use loaded image
.into(new SimpleTarget
@Override public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
view.setBackground(new BitmapDrawable(resource)); // use blurred image
Glide.clear(that); // free resources of intermediate load
}
});
}
})
;
```
Regardless of solution you should handle all lifecycle events, and also use missing album display this way:
// activity
...
+ .error(R.drawable.missing_album_art_blurred)
.into(new SimpleTarget<Bitmap>(300,300) {
+ @Override public void onLoadFailed(Exception e, Drawable errorDrawable) {
+ view.setBackground(errorDrawable);
+ }
+ @Override public void onLoadCleared(Drawable placeholder) {
+ view.setBackground(placeholder); // null if you don't .placeholder(R.drawable.x)
+ }
@Override public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
setBackgroundImage(resource);
}
});
Did you figure it out?
In .load(al.getAlbum_artwork())
what is al or a here
@Shoaib3008757 The model for the album that is a list item, probably a JSON deserialized object. The important thing is that getAlbum_artwork() returns a String, Uri or File.
This is working with me :
Glide.with(context)
.load(imageUrl)
.asBitmap() //needed for obtaining bitmap
.placeholder(R.drawable.placeholderImage)
.into(mImageView);
Then you are free to get bitmap:
Bitmap bitmap = ((BitmapDrawable)mImageView.getDrawable()).getBitmap();
@mabuthraa Keep in mind that the Bitmap will be recycled or re-used if you ever call clear() on mImageView or if the lifecycle (Fragment or Activity) that corresponds to the Context you're passing in to Glide.with() is destroyed.
Most helpful comment
@mabuthraa Keep in mind that the Bitmap will be recycled or re-used if you ever call
clear()onmImageViewor if the lifecycle (Fragment or Activity) that corresponds to theContextyou're passing in toGlide.with()is destroyed.