I am downloading images from server into the ListView and storing it into SD Card.
And when next time listview appears i am accessing it from SD card only using Async method, i
use this approch so that every thing user does not need to access server.
But when all the images are being loaded into listview from SD Card and if i scroll it
pretty fast then every time it tries to access it from the SD Card only rather then from Caches i guess.
I was facing the same problem when images are being downloaded from server also , and thats why i thought to store it into SD Card. but i am facing the same issue.
here is my code ListImageDownloader . In that there is a function called downloadBitmap(String) and i have created another function named downloadSDBitmap(String) whose code is as follows
Bitmap downloadSDBitmap(String urlId) {
Bitmap bitmap = null;
File file = new File(
fileLoc +"/"+ urlId+".png");
if(file.exists()){
Log.d("PATH" , file.getAbsolutePath());
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
apart from this the whole caching code and all are same. so can anyone help me how can i improve it as in a Gtalk android application when i scroll fast it loads the images only once after that if i scroll fast the images remains as it is and doesnt fetch from network
Update
these are my parameters
final static int MAX_ENTRIES = 150;
private static final int HARD_CACHE_CAPACITY =50;
private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
Caching fundamentally relies on available memory. If there is memory left for your application you will need to implement a good solution that caches your bitmaps.
In the past was SoftReference/WeakReference a popular method to cache bitmaps (I did try it a year ago, you can read about my question about this here). But in later APIs of Android the garbage collector has become more aggressive collecting those and therefore they are not no longer recommended.
Now it is recommended to use an LRU cache. There is an example available on the Android developers website.
Related
I want fresco to download and save images to sd-card when connected to internet.
Later when offline and even if cache is cleared, still I need fresco to show saved images.
Is this possible? If yes, how?
Simply saving images to disk cache doesnt seem to work when cache is cleared.
Fresco caches images for you. If you are offline, the images should still be displayed. You should not need to do anything.
However, when the cache is cleared (e.g. when the user presses the button or when device space is low), images are obviously deleted from the cache - which is the desired behavior that should not be changed.
There are 2 options: save selected items, move the cache
Save selected items
If you want to persist selected images (e.g. a "Save" button), you can get the encoded image and save it somewhere on the device.
You should not do this for all images since they will be on the disk 2 times and clearing the cache / uninstalling the app will leave 1 copy on the device.
Something like this could work:
DataSource<CloseableReference<PooledByteBuffer>>
dataSource = Fresco.getImagePipeline().fetchEncodedImage(imageRequest, callerContext);
dataSource.subscribe(new BaseDataSubscriber<CloseableReference<PooledByteBuffer>>() {
#Override
protected void onNewResultImpl(DataSource<CloseableReference<PooledByteBuffer>> dataSource) {
CloseableReference<PooledByteBuffer> encodedImage = dataSource.getResult();
if (encodedImage != null) {
try {
// save the encoded image in the PooledByteBuffer
} finally {
CloseableReference.closeSafely(encodedImage);
}
}
}
#Override
protected void onFailureImpl(DataSource<CloseableReference<PooledByteBuffer>> dataSource) {
// something went wrong
}
}, executorService);
}
More information on how to use the pipeline to get the encoded image: http://frescolib.org/docs/using-image-pipeline.html
Move the cache
Keep in mind that this will persist the cache when it is moved to an external directory, so be careful since this will leave files when the app is uninstalled.
Fresco also allows you to supply a custom DiskCacheConfig and you can create a new DiskCacheConfig.Builder and call setBaseDirectoryPath(File) to change the path to a different folder (e.g. one on the SD card) and you can also change the directory name with setBaseDirectoryName(String)
More information on how Fresco does caching: http://frescolib.org/docs/caching.html
You need to manually save the images to disk when downloaded. When displaying the images, check if image is in disk: if its not, download from url (and save to disk).
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 notice a different behavior in android management of thumbnails.
This is my code:
String[] sProjection = new String[]{MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.DISPLAY_NAME,MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID};
Cursor oCur = oActivity.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
sProjection,"",null,MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
do{
...
Bitmap oBmp = MediaStore.Images.Thumbnails.getThumbnail(oActivity.getContentResolver(),bmpID,
MediaStore.Images.Thumbnails.MICRO_KIND,null);
...
}while (oCur.moveToNext());
And then after loading all thumbnails, I make some elaborations with some of them.
With my mobile (Android 4.1.2) after loading all thumbnails for the first time (for example with new photos or after cleaning cache in Media Storage), "do while" loop is very quick because all the tumbanails are already created and saved in cache.
With another one (same Android version) every time you execute the loop there are no improvements of velocity, because it doesn't save them but it recalculate them all (viewed with the messages in Eclipse LogCat).
Why do this happen?! Is there a way to solve this issue and to force Android save thumbnails in cache memory?
Thanks a lot for the help, any suggestion is greatly appreciated.
Is it possible to do the above?
My scenario is weather graphics with URLs that remain the same, while the underlying image actually changes. Here are the cases I want to cover:
- Inside the same session of my app (typically 2-5min), I never want to reload the images from the web
- After 15 minutes or so, the image has likely changed, and thus even if I have a cached version, I want to dump it.
- When trying to reload images WHILE OFFLINE, any image (including old) is better than no image, so I want to show it from a disc cache.
Is this setup possible? It didn't seem immediately obvious if its feasible with UIL.
Thanks for the great library!
I think this is solution for you:
File cacheDir = StorageUtils.getCacheDirectory(context); // or any other folder
MemoryCacheAware<String, Bitmap> memoryCacheCore
= new LruMemoryCache(4 * 1024 * 1024); // or any other implementation
MemoryCacheAware<String, Bitmap> memoryCache
= new LimitedAgeMemoryCache<String, Bitmap>(memoryCacheCore, 15 * 60);
DiscCacheAware discCache = new LimitedAgeDiscCache(cacheDir, 15 * 60);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCache(memoryCache)
.discCache(discCache)
...
.build();
UPD: UIL always search needed image in memory cache at first. Then UIL search it in disc cache. And then it downloads image from network.
If you use "limited age" memory cache or disc cache then bitmap or image file will be deleted from cache after timeout (actually they will be deleted during search in cache).
Logic is following:
Search bitmap in memory cache
needed bitmap is there
bitmap was added to cache more than specified time ago
delete it from memory cache, go to step 2
bitmap was added to cache recently
get the bitmap, display it. End.
no needed bitmap in cache, go to step 2
Search image file in disc cache
needed image is there
image was added to cache more than specified time ago
delete it from disc cache, go to step 3
image was added to cache recently
decode image to bitmap, display it. End.
no needed image in cache, go to step 3
Download image
Don't forget enable caching (in display options, DisplayImageOptions).
I am using following example to display internet images in my activity.
http://developer.android.com/resources/tutorials/views/hello-gridview.html
In custom image adapter I'm directly loading images from internet and assigning it to imageview.
Which shows images in gridview and every thing works fine but it is not efficient way.
When ever i scroll gridview it again and again loads images and thats why gridview scrolls very slow
Is there caching or some useful technique available to make it faster?
Create a global and static method which returns a Bitmap. This method will take parameters: context,imageUrl, and imageName.
in the method:
check if the file already exists in the cache. if it does, return the bitmap
if(new File(context.getCacheDir(), imageName).exists())
return BitmapFactory.decodeFile(new File(context.getCacheDir(), imageName).getPath());
otherwise you must load the image from the web, and save it to the cache:
image = BitmapFactory.decodeStream(HttpClient.fetchInputStream(imageUrl));
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File(context.getCacheDir(), imageName));
}
//this should never happen
catch(FileNotFoundException e) {
if(Constants.LOGGING)
Log.e(TAG, e.toString(), e);
}
//if the file couldn't be saved
if(!image.compress(Bitmap.CompressFormat.JPEG, 100, fos)) {
Log.e(TAG, "The image could not be saved: " + imageName + " - " + imageUrl);
image = BitmapFactory.decodeResource(context.getResources(), R.drawable.default_cached_image);
}
fos.flush();
fos.close();
return image;
preload a Vector<SoftReference<Bitmap>> object with all of the bitmaps using the method above in an AsyncTask class, and also another List holding a Map of imageUrls and imageNames(for later access when you need to reload an image), then set your GridView adapter.
i recommend using an array of SoftReferences to reduce the amount of memory used. if you have a huge array of bitmaps you're likely to run into memory problems.
so in your getView method, you may have something like(where icons is a Vector holding type SoftReference<Bitmap>:
myImageView.setImageBitmap(icons.get(position).get());
you would need to do a check:
if(icons.get(position).get() == null) {
myImageView.setImageBitmap(defaultBitmap);
new ReloadImageTask(context).execute(position);
}
in the ReloadImageTask AsyncTask class, simply call the global method created from above with the correct params, then notifyDataSetChanged in onPostExecute
some additional work may need to be done to ensure you don't start this AsyncTask when it is already running for a particular item
You will need to implement the caching yourself. Create a proxy class that will download the images. In the getView ask this class to download an image by passing a url. In the proxy class create a HashMap that will map a url to a Bitmap. If the key for the passed url doesn't exist, download the image and store it. Otherwise returned the stored bitmap converted to an imageView.
Of course you can't afford to store as many images as you like. You need to set a limit, for example 10 images, based on the image size you expect to have. When the limit is exceeded, you need to discard old images in the favor of new ones.
You could try DroidFu. My app uses the ImageCache. There's also some manner of web-based imageview or something of the sort in the library. See in particular WebImageView and WebGalleryAdapter: http://mttkay.github.com/droid-fu/index-all.html
Edited to add: The droid-fu project is deprecated in favor of Ignition. https://github.com/mttkay/ignition