I am trying to make a recyclerview with gifs. Everything shows perfectly but fresco do not cache gifs. After scroll recycler down and scroll up again, gifs are loading once again. I supposed they should be cached and loaded a bit quicker. Previously I used ION library. Loading was quicker and did not have cache problem. I had to change lib because, it has some problem with gif decoding, described here. Current solution looks like that:
//for default initial in application class
Fresco.initialize(this);
//I have also tried to change DiskCacheConfig and ImagePipelineConfig params.
//Without any positive result
//for recyclerview on onBindViewHolder
GenericDraweeHierarchy hierarchy = holder.draweeView.getHierarchy();
Uri uri = Uri.parse(path);
hierarchy.setPlaceholderImage(R.drawable.img_bg);
Logger.e(check(uri) + " " + uri.toString());
DraweeController controller = Fresco.newDraweeControllerBuilder().setUri(uri)
.setAutoPlayAnimations(true).build();
holder.draweeView.setController(controller);
//for method which show cached uri images in imagepipeline
public static boolean check(Uri uri) {
ImagePipeline imagePipeline = Fresco.getImagePipeline();
return imagePipeline.isInBitmapMemoryCache(uri);
}
//... all the time log shows "false + gif url"
I have not seen any information about not caching animated images. There is information about not supported image postprocessing for animations, but it's everything about that. How to correctly cache gifs?
edit:
It looks like fresco cache animations, because below method return true for reloaded gifs.
public static boolean isImageDownloaded(Uri loadUri) {
if (loadUri == null) {
return false;
}
CacheKey cacheKey = DefaultCacheKeyFactory.getInstance()
.getEncodedCacheKey(ImageRequest.fromUri(loadUri));
return ImagePipelineFactory.getInstance().getMainDiskStorageCache().hasKey(cacheKey)
|| ImagePipelineFactory.getInstance().getSmallImageDiskStorageCache()
.hasKey(cacheKey);
}
Just to make things a bit more clear, Fresco has 3 levels of cache:
DiskCache - Keeps image files in their original format. (To be
precise, if on an Android version that doesn't fully support webp
images, they may be transcoded to other format before storing.)
EncodedMemoryCache - In-memory cache of images in their original encoded format. (Image is kept as a byte-array of the original bytes
as they are stored on disk + some additional metadata.)
BitmapMemoryCache - In-memory cache consisting mostly of Android Bitmaps. Bitmaps are decoded images and each pixel occupies 32bits
which is significantly more than what it takes when encoded.
The trade-off is obviously space vs time. Available memory is limited and if the image is not in the bitmap cache, it will have to be decoded again. Furthermore, if it is not in the encoded memory cache either, it will have to be read from the disk which also can be slow.
Now back to the animated images. This is a known limitation. Animated images are not cached in their decoded form because that would exhaust the bitmap cache (just multiply the num_frames * width * height * 32bpp) and a single animated image can possibly evict every other image in the cache. Instead they are decoded on demand and only a couple of frames that are about to be displayed next are kept in a short-lived cache.
We have some plans to improve animations, although I cannot provide any time estimates.
I was facing the same issue, it appears that Fresco is correctly caching the gif images, but in fact it's taking time to decode and play the animation each time you scroll through the RecyclerView or any other view you're using.
However if you use a gif image without animation, the gif image doesn't "reload" when scrolling through the view.
Since I have control over the images displayed inside my app. I am creating 2 versions of the gif images on my server, the first without animation to display inside the RecyclerView/ListView and the other to display inside the "Media viewer" activity, when the user clicks on an item in the list.
Fresco, they mainly focused low size gifs. I tried with low size gifs in recycleview, caching working perfectly. If you use high resolution gifs, they need high memory consumption for decode and caching. may be surpass heap size. so they are decoded on demand, and only a couple of frames (those about to be displayed) get cached.
It is possible to configure fresco to display the first frame as soon as it is available (without decode whole frame) and cache the static first frame easily.
ImageDecodeOptionsBuilder b = new ImageDecodeOptionsBuilder();
b.setForceStaticImage(true);
ImageDecodeOptions imageDecodeOptions=new ImageDecodeOptions(b);
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(animatedGifUri).setImageDecodeOptions(imageDecodeOptions).setLocalThumbnailPreviewsEnabled(true).build();
Related
I'm maintaining an android app that uses Glide to download images.
Besides images, sometimes it also displays videos, and uses the same process to display the video thumbnail.
I noticed in this cases, Glide correctly extracts and displays a thumb, BUT it always downloads the entire video do do that, which is not convenient.
My question is: Is it possible to make Glide stop downloading after reading the first frame? Or Glide is not recommended for this job?
This is a test code.
I also tried adding .frame(30_000_000L) in the requireOptions.
btn.setOnClickListener{
// a local server just for testing
val url = "http://192.168.1.6:5676/roxo.mov"
Glide.with(this)
.asBitmap()
.load(url)
.apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
.override(200, 200)
.into(img_view)
}
Scenario:
I have a large GIF image which I want to cache the first time user opens the app using Glide - Image Loading and Caching library. After that whenever user opens the app, I want to show the cached version if present. This GIF URL will expire after a given interval. When it expires, I fetch the new GIF URL and display/cache that for future use.
What I tried:
I went through Caching and Cache Invalidation on Glide's github page. I also went though the Google Group thread Ensuring That Images Loaded Only Come From Disk Cache, which shows how to get the image form cache. I also went through How to invalidate Glide cache for some specific images question.
From the links above I see the following code sniplet which shows how to load the image from cache. However this only tries to get the image from cache. If its not present in cache, it doesn't try to get from the network and fails:
Glide.with(TheActivity.this)
.using(new StreamModelLoader<String>() {
#Override
public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1) {
return new DataFetcher<InputStream>() {
#Override
public InputStream loadData(Priority priority) throws Exception {
throw new IOException();
}
#Override
public void cleanup() {
}
#Override
public String getId() {
return model;
}
#Override
public void cancel() {
}
};
}
})
.load("http://sampleurl.com/sample.gif")
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(theImageView);
Questions:
Is there a cleaner way to achieve the following: Show the GIF image from the cache if present else download the GIF, cache it for later use and show it in the ImageView.
The caching article above mentions the following:
In practice, the best way to invalidate a cache file is to change
your identifier when the content changes (url, uri, file path etc)
The server sends a different URL to the app when the previous one expires. In this case, I believe the old image will eventually be Garbage Collected? Is there a way to force remove the image from the cache?
On a similar note, is there a way to prevent the Garbage Collection of an image with specific key (to prevent downloading the large file again) and then later instruct to delete the old image from cache when the URL changes?
You don't need a custom ModelLoader to show the GIF from cache if present and fetch it otherwise, that's actually Glide's default behavior. Just using a standard load line should work fine:
Glide.with(TheActivity.this)
.load("http://sampleurl.com/sample.gif")
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(theImageView);
Your code will prevent Glide from downloading the GIF and will only show the GIF if it is already cached, which it sounds like you don't want.
Yes, the old image will eventually be removed. By default Glide uses an LRU cache, so when the cache is full, the least recently used image will be removed. You can easily customize the size of the cache to help this along if you want. See the Configuration wiki page for how to change the cache size.
Unfortunately there isn't any way to influence the contents of the cache directly. You cannot either remove an item explicitly, or force one to be kept. In practice with an appropriate disk cache size you usually don't need to worry about doing either. If you display your image often enough, it won't be evicted. If you try to cache additional items and run out of space in the cache, older items will be evicted automatically to make space.
Glide.with(context)
.load("http://sampleurl.com/sample.gif")
.skipMemoryCache(true)
.into(imageView);
You already noticed that we called .skipMemoryCache(true) to specifically tell Glide to skip the memory cache. This means that Glide will not put the image in the memory cache. It's important to understand, that this only affects the memory cache! Glide will still utilize the disk cache to avoid another network request for the next request to the same image URL.for more read this
Glide Cache & request optimization.
Happy coding!!
I have a Custom AdapterView (sort of) in which I lazy load images. To do it, I use the awesome aquery library.
Short story: I would like to cache (memcache and filecache) the downsampled version of the file. It makes it quicker to add to my adapter - when the image is small I have no lags when scrolling my AdapterView. When the image is big, even if I use downsampling it lags a bit. I found out, that aquery stores the full version of image and downsamples it every time I call aq.image(...). How to cache the resized version, not the original one?
Long story:
My AdapterView relies heavily on images. These images are rather big, and when adapter items gets instantiated it takes some time to downsample it and then add to the list. So I thought it would be nice, to instantiate items with a lo-res photo when scrolling, and only load the hi-res version when the scrolling stops. It works like a charm, when I use two separate image urls (one for thumnbail and another for the original image). But the API I work with is quite limited, so I won't have the thumbnail images' urls. I have to async download the big version, downsample it, save both big and small version and then use whichever I need. And here the "short story" begins.
I just released an open source library called droidQuery which is a full port of jQuery to Android. It is much simpler to use than AQuery, and provides a lot more configurations. To download an image and set the output size, and cache the small image (on a per-session/every ten minute basis), you can use the following code:
final ImageView image = (ImageView) findViewById(R.id.myImage);
$.ajax(new AjaxOptions(url).type("GET")
.dataType("image")
.imageHeight(200)
.imageWidth(200)
.context(this)
.cache(true)
.cacheTimeout(600000)
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
$.with(image).val((Bitmap) params[0]);
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
droidQuery.toast("could not set image", Toast.LENGTH_SHORT);
}
}));
I also used AQuery library in the past, but after encountering some problems with limited configuration and weird progresbar visibility issue, I moved to Android-Universal-Image Loader
https://github.com/nostra13/Android-Universal-Image-Loader it gives you your needed feature as well as plenty other useful configuration options.
Just read this site from top to bottom - and you should be able to run it in a minute.
In your case most interesting lines are
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
.discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75)
.discCache(new UnlimitedDiscCache(cacheDir)) // default
.discCacheSize(50 * 1024 * 1024)
.discCacheFileCount(100)
You can also change cached file names.
I am trying to use a disk cache (not a memory cache) so i download my images from an urls and put it in a grid view. I want to download my images only one time.
I found this example (bitmapFun) in google site: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
I found this example a bit complicated.
There are many objects in util package (AsyncTask, DiskLruCache, ImageCache, ImageFetcher, ImageResizer, ImageWorker, Utils)
Is there a way or a tutorial that show how can i use a disk Lru cache without using all those object.
I don't want to resize my image and i was not able to remove ImageResizer class.
Here you have good answer :Android image caching. Quotation :
"Consider using Universal Image Loader library by Sergey Tarasevich. It comes with:
//Multithread image loading. It lets you can define the thread pool size
//Image caching in memory, on device's file sytem and SD card.
//Possibility to listen to loading progress and loading events
Universal Image Loader allows detailed cache management for downloaded images, with the following cache configurations:
UsingFreqLimitedMemoryCache: //The least frequently used bitmap is deleted when the cache size limit is exceeded.
LRULimitedMemoryCache: //The least recently used bitmap is deleted when the cache size limit is exceeded.
FIFOLimitedMemoryCache: //The FIFO rule is used for deletion when the cache size limit is exceeded.
LargestLimitedMemoryCache: //The largest bitmap is deleted when the cache size limit is exceeded.
LimitedAgeMemoryCache: //The Cached object is deleted when its age exceeds defined value.
WeakMemoryCache: //A memory cache with only weak references to bitmaps.
A simple usage example:
ImageView imageView = groupView.findViewById(R.id.imageView);
String imageUrl = "http://domain.com/image.png";
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
imageLoader.displayImage(imageUrl, imageView);
This example uses the default UsingFreqLimitedMemoryCache.
I have trouble when I'm using Glide in my app. As I've understood, if image was downloaded once and I request image from cache from other activity, Glide must show image quick. And I got this behavior, but not in my app. Image loads very slow (about 3 seconds), although in another app it was about 0.4 second.
My code with calling Glide:
Glide.with(this)
.load(url)
.signature(new StringSignature(url))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(mHeader);
And in other activity code are same.
May you help me?
Thanks
You don't need the signature(url) part, the model (url String in your case) is already a part of the cache key.
The problem may be that your header changes size. The view size (= resulting Bitmap size) needs to be constant for a cache hit. However since you're doing ALL caching the load should still be fast. Is there anything changing in the url maybe, like a sessionid or similar? That would make the cache miss.
If the url you're loading is an animated GIF RESULT caching can be the culprit, here's a reference.