I have the user's profile pic across multiple activities in my app. Once, they change their profile image, I want to make sure that all my Glide instance's cache are cleared. That way when they navigate around the app, they can see their updated profile pic.
Currently I'm using this method: Glide.get(activity).clearDiskCache(); and that only clears the Glide cache for that activity and not across my app.
Hope someone has a quick solution, where I don't need to call the .signature() function for each glide instance in each of my activites. Or clear each glide cache in each activity.
Try
Glide.get(context).clearMemory();
OR
Glide.get(context).clearDiskCache();
Note: clearMemory() must be called on the main thread. clearDiskCache() must be called on a background thread. You can't call both at once on the same thread.
I went through this whole windmill trying Signatures and clearing caches, and to be honest - none of those options work particularly well and they're usually slow.
Glide's first recommended solution is bar far superior, although it can sometimes take a bit more time to rework your code. I eventually lost my marbles and made the necessary changes to my code. It was well worth it.
Solution: Change the image name of the image when the user uploads a new image. Get the file name and use that. Once the image URL has changed, Glide understands you have changed the image and will update the Cache accordingly.
Related
I am designing an Android application targeting >= API 17. I have created a class, DownloadImageTask which extends AsyncTask, and receives a string (URL) and an ImageView as arguments. In it, I am opening an HTTP connection, downloading an image from a URL, and using BitmapFactory to create a Bitmap object from the data, then setting the bitmap to the ImageView. The end result is a populated list of data which is available to the user to scroll through, with images populating as they can.
This appears to be a good design on the surface - but I am concerned that I am putting my app at risk for an OOM condition, or other violation of the user experience rules. I'd like to know if the way I've designed this is correct, or if not, how I should approach this.
Thank you in advance for your help.
Two considerations to your own approach:
You shouldn't pass the ImageView to the async task because in that way you are coupling your view and your service layer. So send to the async task the URL, and onPostExecute method call to Activity which implement an updateView (or the like) method.
About your OOM, you are right. The problem might arise if you use the original bitmaps which could have larger resolution than required. Therefore you should scale down the images you keep in memory.
The last issue might not be difficult if you use a few images otherwise could be problematic. So if you will be working with a lot of images and you are not forced to implement your own version, you should have a look to the existing libraries. Some are already mentioned:
Glide
Picasso
When I use ListView the getView() method is called many times. Every time when the getView() is called i load the image with Asyc task. I mean every time i reset the image which is annoying.
How to understand when to load the image?
You should cache loaded images, by storing i.e. on SD card, so once you got a copy there, no need to download it again. There's lot of ready-to-use classes that can do the job for you, like:
http://greendroid.cyrilmottier.com/reference/greendroid/widget/AsyncImageView.html
you must must have two flags.
One which says if you've already loaded the image, if true you do nothing.
One which says if you're currently loading the image, if true you do nothing.
The members will also help you on maintaining the state of the image.
Your code should look something like this:
private boolean isLoading = false;
private boolean hasLoaded = false;
if(!hasLoaded){
if(!isLoading){
isLoading = true;
//do async load
//on positive completition callback set hasLoaded to true
//on negative completition callback set isLoading to false
}
}
One of the best solution is to create image cache using the WeakReference. This way you can keep images in memory and only need load from server when they are not in memory. In this method the image would be removed from the memory when system encounter low memory situation. So your current activity would always keep the hard reference to the bitmap's required and the image cache would keep the weak reference to the bitmap's.
below reference links will help you
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
http://www.codeproject.com/Articles/35152/WeakReferences-as-a-Good-Caching-Mechanism
the Volley library (made by google) has a very intuitive class for an imageView that can have a url , called "NetworkImageView" .
you should check it out and watch the video, since they show that it's quite annoying to do it using asyncTask (plus the asyncTask is known to have a limit of tasks, about 255 or so) .
for setting the url, just use setImageUrl .
it has some useful methods for the phases of loading too: setDefaultImageResId , setErrorImageResId.
it's also supposed to have built in caching mechanism of some sort, but i haven't read much about it, so you might want to check out their samples.
this will remove the need to use asyncTasks for the listView's items.
one of my questions regarding the volley includes a sample code , here .
You can add a caching layer and optionally preloading the images. A good strategy for caching Images (Bitmap objects to be exact) is to use a strategy called LRU or least recently used.
Android support library has a class called LruCache that implements this strategy. So for example, when you download/load the image for the first time, you stick it into the cache. later, you can first check if it's already in cache and load it from there.
For preloading, A good rule of thumb is to preload the previous ten and the next ten items.
I have simple android app. App includes around 50 images, mostly PNG format, but also most of them are icon size. There is about 5 layouts and use Google maps and WebView. App itself weights bit under 6MB.
Then i run all of app options and then i look into app settings -> cached processes, i see my app uses 70 MB of RAM !!!
Im calling finish() method wherea i can, but that doesnt help. What could help to reduce memory use ?
Even Angry birds is using less !
It might be that you're loading your Bitmaps with methods that create immutable Bitmaps and not releasing them adequately (you have to take care to dispose of all of their references or else the garbage collector will never free the space they're using.
If that's the case, this reference might help you with the Bitmaps you're using.
The best way is to use 9 patch PNG images for your application. This will definitely help you to reduce cache size as well as application size. More over deploy you application code according to android life cycle, excessive use of finish() make the activity to onpause() state, so use onDestroy() where you want to kill that activity.
Create a drawable folder and put all those 9 patch image there.Deploy onDestroy() to fill the activity
Please compress each and every image without affect image quality and then take care of leaked memory. Each activity action completed then finish that activity. once you exit from application. destroy all running methods. if you need to clear memory manually, by calling the .recycle() method of your bitmap object.I think its help you to reduce memory.
Try something like this, it may help i guess:
#Override
protected void onDestroy() {
super.onDestroy();
unbindDrawables(findViewById(R.id.LayoutId));
System.gc();
}
Could someone tell me how to make a good mechanism for async. download of images for use in a ListView/GridView?
There are many suggestions, but each only considers a small subset of the typical requirements.
Below I've listed some reasonable factors (requirements or things to take into account) that I, and my collegues, are unable to satisfy at once.
I am not asking for code (though it would be welcome), just an approach that manages the Bitmaps as described.
No duplication of downloaders or Bitmaps
Canceling downloads/assigning of images that would no longer be needed, or are likely to be automatically removed (SoftReference, etc)
Note: an adapter can have multiple Views for the same ID (calls to getView(0) are very frequent)
Note: there is no guarantee that a view will not be lost instead of recycled (consider List/GridView resizing or filtering by text)
A separation of views and data/logic (as much as possible)
Not starting a separate Thread for each download (visible slowdown of UI). Use a queue/stack (BlockingQueue?) and thread pool, or somesuch.... but need to end that if the Activity is stopped.
Purging Bitmaps sufficiently distant from the current position in the list/grid, preferably only when memory is needed
Calling recycle() on every Bitmap that is to be discarded.
Note: External memory may not be available (at all or all the time), and, if used, should be cleared (of only the images downloaded here) asap (consider Activity destruction/recreation by Android)
Note: Data can be changed: entries removed (multi-selection & delete) and added (in a background Thread). Already downloaded Bitmaps should be kept, as long as the entries they're linked to still exist.
setTextFilterEnabled(true) (if based on ArrayAdapter's mechanism, will affect array indexes)
Usable in ExpandableList (affects the order the thumbnails are shown in)
(optional) when a Bitmap is downloaded, refresh ONLY the relevant ImageView (the list items may be very complex)
Please do not post answers for individual points. My problem is that that the more we focus on some aspects, the fuzzier others become, Heisenberg-like.
Each adds a dimension of difficulty, especially Bitmap.recycle, which needs to be called during operation and on Activity destruction (note that onDestroy, even onStop might not be called).
This also precludes relying on SoftReferences.
It is necessary, or I get OutOfMemoryError even after any number of gc, sleep (20s, even), yield and huge array allocations in a try-catch (to force a controlled OutOfMemory) after nulling a Bitmap.
I am resampling the Bitmaps already.
Check this example. As Its is used by Google and I am also using the same logic to avoid OutOfMemory Error.
http://developer.android.com/resources/samples/XmlAdapters/index.html
Basically this ImageDownlaoder is your answer ( As It cover most of your requirements) some you can also implement in that.
http://developer.android.com/resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html
In the end, I chose to disregard the recycling bug entirely. it just adds a layer of impossible difficulty on top of a manageable process.
Without that burden (just making adapters, etc stop showing images), I made a manager using Map<String, SoftReference<Bitmap>> to store the downloaded Bitmaps under URLs.
Also, 2-4 AsyncTasks (making use of both doInBackground and onProgressUpdate; stopped by adding special jobs that throw InterruptedException) taking jobs from a LinkedBlockingDeque<WeakReference<DownloadingJob>> supported by a WeakHashMap<Object, Set<DownloadingJob>>.The deque (LinkedBlockingDeque code copied for use on earlier API) is a queue where jobs can leave if they're no longer needed. The map has job creators as keys, so, if an Adapter demands downloads and then is removed, it is removed from the map, and, as a consequence, all its jobs disappear from the queue.
A job will, if the image is already present, return synchronously. it can also contain a Bundle of data that can identify which position in an AdapterView it concerns.
Caching is also done on an SD card, if available, under URLEncoded names. (cleaned partially, starting with oldest, on app start, and/or using deleteOnExit()
requests include "If-Modified-Since" if we have a cached version, to check for updates.
The same thing can also be used for XML parsing, and most other data acquisition.
If I ever clean that class up, I'll post the code.
I currently have a ListView with a custom adapter that gets information describing the content of the rows asynchronously. Part of each row is an image URL, that I'm planning to download asynchronously and then display.
My current plan for a strategy to download these images is:
Keep a cache of soft references to downloaded Bitmap objects.
When a getView() is called and the bitmap is in the cache, set the bitmap for the ImageView directly.
If the bitmap isn't in the cache, start loading it in a separate thread, after the download is complete add it to the cache and call notifyDataSetChanged() on the adapter.
I am also planning to kill pending downloads when the Activity object owning the ListView's onDestroy()-method (Or possibly even in the onPause()-method) is called, but most importantly I want to kill the download of pending images when the row goes off screen. I might only actually cancel the download after a short delay, so it can be resumed without wasting bandwidth if the row comes on-screen quickly again.
I, however, am unsure about a few things:
What is the best way to detect when a row goes off-screen so I can cancel the download?
Is calling notifyDataSetChanged() the best thing to do after the download has completed or is there a better way?
Also any comments on the whole strategy would be appreciated.
I don't think calling notifyDataSetChanged() is really needed... I would do it like that:
store URL as Tag in the view when created/updated
register a listener in downloader thread (async task???) for download keeping reference to the view and the URL
whenever image is downloaded asynchronously, I check TAG in the view and if it matches - i would update the ImageView (important to do it in UI thread, but when using async task, it is given). The image should also be stored on SD card (and every time you request URL you should check if it is not already downloaded).
every time when getView() reuses the view (passed view is not empty) I would check the Tag (old URL), replace it with the new URL and cancel the download of the oldURL.
I think it would be pretty much it (some corner cases might happen)...
I use the getFirstVisible and getLastVisible AdapterView properties to detect the visible rows, and put requests in a fixed size stack.
My project is open source and has a most permissive license, if you want to use it:
https://github.com/tbiehn/Android-Adapter-Image-Loader
-Travis
I found the remote resource managing / fetching in the Foursquared source code to be pretty helpful:
http://code.google.com/p/foursquared/source/browse/main/src/com/joelapenna/foursquared/util/RemoteResourceManager.java
It caches images on disk and handles all 3 of your feature requests. See an adapter for how to use it.
As for canceling a download when a row goes off screen you'll have to handle that yourself