The question regarding thumbnail creation seems to get asked a great deal, and each time there seems to be a great many different solutions proposed. From my experience none of the supplied answers are actually sufficient and all seem to have their own flaws.
a) Memory issues when re-sizing
b) Performance issues / slow to render
I therefore wanted to post a question regarding a very common functional requirement that I am hoping someone can assist with.
'User generates a report using activity and takes associated photo using internal camera. The report along with the image path is saved to SQLLite database. User wants to view reports in a scrollable list view at some point in the future. Number of reports could be 1..100'
So given these requirements the questions I have are as follows
1) Given that to data I have not found a reliable / performent / memory optimised way of resizing full size images at run time to a given thumbnail, should I create a smaller image at image capture time?
2) If you need to display many items in a ListView should you use paging or should it be possible to display a 100 items in one ListView?
Thanks
Regarding number 2: What you could do is load a subset first (say the first 30, depending on your listview item size), and load the rest asynchronously and append them to your adapter. This will decrease the loading time of your screen.
1/ Why do you want to reinvent the wheel :) There are libraries that already do for you and do it well. See picasso or AQuery for instance (there are other libraries too).
Basically, they have a thumbnail cache somewhere on the SDcard/internal memory to keep last N requested thumbnails.
I don't know if you are keeping the image data within the database, in which case you may have to adapt one of those libraries to load the image data from the DB and not from a file as it is usually the case.
2/ Why do you want to reinvent the wheel :) There are libraries that already do for you and do it well. See https://github.com/commonsguy/cwac-endless for instance (and google for more)
Related
I'm currently building an app for learning purpose (new to android programming) that lets the users to upload
image to the server and watch other users images (by swipe the screen for example).
I finally succeeded to let the user upload image to the server and I was wondering how to code the part that retrieve the images from the server and present them in the app.
Assuming I have at the time million images, I don't want the app to load all the images in the same time because it will take a lot of time.
And all the guides I'v seen makes the app to load all the images at once.
So my question is what is the recommended way to do so?
Hope I was clear.
Thanks.
Let's say you have an array with a million images in your server to load in your app that will be shown in a RecyclerViewand assuming a screen can fit about 5 images.
In this case scenario a good approach would be to fetch 10 images at a time and load them in a RecyclerView. Load the first 10 images and once the user reaches the end of the scroll load some more images, add them to the previous retrieved image list, and finally update your RecyclerView to present them. With some effort you may be able to create a Facebook look alike effect.
RecyclerView has methods that will easily help you detect the end of scroll (there are plenty of ways you can find in StackOverFlow). Every time you detect an end of scroll, just upload more images.
This is one example: How to know whether a RecyclerView / LinearLayoutManager is scrolled to top or bottom?
Use FlexBox Layout for better UI Design to show images
https://github.com/google/flexbox-layout
See how this cat images are shown using FlexBox Layout
Is there a general guideline or rule of thumb on when I should cache a photo?
For example I have a grid view of smaller icon images. This page isn't at the beginning of the app so there's no guarantee a user sees it, these images could change server-side but for the most part they won't. Should I cache them because there are so many or not worry about them because they are small?
Another example would be if I click on one of the images mentioned above it will lead me to a view with one larger image, should I cache this image since it's larger or should I let the user make a network request each time?
I'm not looking for an answer for these specific questions, but rather a guideline to follow.
To clarify by caching I mean saving the file to storage. Also, is there a better method than this?
Thanks for the help!
Caching is an important feature in user experience, so even if there's no guarantee the user sees that particular 'page', it a nice thing to have.
Saving images in a cache depends directly on many variables:
- How much time does it take to regenerate images if not cached
- How many images are we talking about
- How often do they change
- What size is each image
For example, having 100 thumbnails of images that change every month makes a lot of sense. Caching 200 images with 1024x768 resolution that change very often doesn't.... or does it? The answer is very variable.
Also cache should always have storage boundaries. Even in the previous example with the large images, you could limit the cache to a a certain storage capacity.
Normally the cache saves a local copy of the most recently used items. But you can implement a more sophisticated algorithm, for example, saving the most visited images instead of caching the most recently used.
I'm using Jake Wharton's DiskLruCache lib.
I'm curious about app performance, caching strategy, using caching both in a view and across a whole application. Most of the time, the image won't change.
Let's say for example I have a 320x320 photo on my server. I open the stream, save the image.
In my list views, I show bitmaps and in the detail, I show a larger image. Should I save a thumbnail bitmap too? Is that more efficient?
What is your experience with sharing the cache "object" across the entire app (let's say I have multiple views that might leverage the same data. What are the issues with this?
For the sake of performance and currency, what if the image changes on the server. What's the best strategy to know that it's changed? I don't have access to modified date. Only size and yet, I don't really want to query size every time either. Set a flag in the app on the server and then query the flag?
In a traditional application (if there is such a thing), what's the best practice for clearing the cache from time to time? (indent weirded out.)
(I was inspired to write this after seeing all of the performance improvements by Facebook in iOS. I don't have billions to do caching but I would like to at least be smart about it! LOL)
A lot of these answers depend on the type of app you're writing, how important image updates are (and how likely images will change, etc), and total images produced. Aside from disk caching, you should be using memory caching as well, especially in ListViews and other areas where the same images will be scrolled through repeatedly. Look at LruCache and read the Caching Bitmaps entry from Google.
320x320 is probably too big for a listview, you will likely want to create thumbnails (depending on device, and how you're implementing your listviews).
1) You should be using Disk caching fairly aggressively (how you define that is up to the app you're writing). Use the external storage directory, and if they have a few GB left over, it's not an issue if you take 100 mb for your app for instance. It can all be cleared away if it's ever needed anyway.
2) There shouldn't be an issue. Disk IO (even to a flash medium) should never be handled on the main thread. Use AsyncTasks to load the images. There can only be one main foreground activity at once anyway, and while an activity is sleeping, it shouldn't be trying to read from the disk anyway.
3) Again this depends on how you're implementing your app. You should be able to get additional information when retrieving the file (even Apache can tell your app the last modified date).
3.1) You can have a sqllite db that keeps track of how often certain images are used, and most recent read. If the most recent read is a few days old, let that image expire.
Edit: There's a library from Jake Wharton now called Picasso that I would recommend using, which handles the Network/local IO as well as memory and disk caching. Check it ou here: http://square.github.io/picasso/ . A lot of what you will need to do can be accomplished with one line: Picasso.with(this).load(imageFileURL).into(imageView);
I have to believe there's a way to clear image data from memory once it's no longer required, but despite exhaustive searching I can't find a solution. Both stack and the google android dev list are full of questions regarding OOM errors, specifically "bitmap size exceeds VM budget", but I still don't see a clear answer.
I understand there are hard memory limits on devices, and I understand it's not realistic to load up and display or cache large amounts of image data, but there should be away to discard data that's no longer required.
For example, imagine this very basic hypothetical app, that emulates a lot of the behavior of the native gallery app:
An image gallery that allows the user to peruse images from a remote server.
There might be any number of images on that server.
The app displays 1 image at a time, and allows a user to go back or forward 1 image at a time through button presses or swiping.
There'd be a maximum of 3 images rendered at any one time (so the user can see the one immediately to the left or right of the current image when swiping). All other image data should be discarded.
Images are loaded using URL.openStream and Drawable.createFromStream or BitmapFactory.decodeStream. Streams are closed appropriately.
Images are sized appropriately on the server before being fetched.
Loading happens in AsyncTasks. Tasks that are no longer needed (due to moving away from an image with an incomplete task) are cancelled. Any references in the AyncTask are WeaklyReferenced.
When any image is no longer required, it's "cleared" via:
getBackground().setCallback(null)
Listeners are set to null
setImageDrawable/Bitmap(null)
removeView
This simple construct, that takes into account all the suggest practices I'm aware of, will inevitably crash with an OOM error at some point. Using BitmapFactory.Options inSampleSize and inPreferredConfig will delay the inevitable, but not forever, and at the cost of image quality. In this example, I've used remote images, but the issue exists with images stored in /assets/ or in internal memory, etc.
My feeling is that if we can display X amount of image data at one point, and we take all steps to remove that image data from memory, we should be able to display that same amount of data later, without having to compensate for what has happened before.
With the sheer quantity of questions about this very issue, I'd hope to have a standard solution documented, but if there is one, I can't find it. I've seen answers posted by Romain Guy, who otherwise seems very generous with his knowledge and active in the community, that say something like "Simple. Don't use so much memory". OK. Tell me how.
I should also mention that System.gc does nothing to help this. I'm also aware of bitmap.recycle, but unless I'm mistaken this can't be used in this fashion.
Am I missing something fundamental? Is there a way to discard image data once it's no longer being used? What is missing from the above to create a simple photo gallery? Assuming the built-in gallery app uses the framework and not the NDK, I imagine there has to be a way...
TYIA.
/this question has also been posted on the android developer google group list.
Through my work with Prime I found a few tips, one of which you have not mentioned. When you decode your Bitmaps make sure to use the inPurgeable and inInputShareable flags in your BitmapFactory.Options. That will help a little bit but I would recommend you look at my implementation of image loading in Prime. I use it in all of my products without any memory issues. I have found that 95% of memory problems are from the incorrect usage of the Bitmap class.
There is a very detailed article about the use of bitmaps on the android developer website.
Did you look at it ?
It explains how to load, cache and display bitmaps efficently and how to get rid of this famous OutofMemoryError.
There is also a sample application from an image gallery.
I think that's what you're looking for.
I am very familiar with OOM concept. I have come across so many questions that have been asked here and I know this could sound like a repeated question on OOM, but I have a different problem now.
I have hundreds and hundreds of images, which I have to show in a single ListView in a single Activity.
I have made use of the Fedor's lazy loading concept.
The code works fine when the images are little(something like 50-100 images), but if I get around 200 to 300 images, as soon as the user makes a scroll through the ListView (eventually) I am getting an out of memory exception.
I am using the efficient Adapter concept as shown here in developer site and I believe that this is the best custom adapter usage method.
All the suggestion what I have seen so far is based on reducing the bitmap size by scaling it or some other means. I have already tried those methods. But still the OOM occurs in some point of time.
Reducing the quality of the image doesn't look like the way to handle this Exception , because which seems to affect user experience and, also, it doesn't work for a large amount of bitmaps.
My questions here are:
How do you display n images in a single ListView without facing OOM?
What are the best strategies to follow in this scenario?
Is it possible to show around 500 images in a single ListView?
Any help is much appreciated.
Look into googles shelvesproject.
Shelvesproject does following: uses a placeholder image while loading image, use weakreference for storring images, delayed handler updated with clearing.
Yes and no, it is possible to have listview with this many items but its not possible to have this many images all in memory at the same time.
Try with API Level 11+ images before this API Level are placed in application heap. And yes it is possible to have a List View with a 500 images and working smooth (Contacts application for example).
This http://code.google.com/p/android-imagedownloader/ may help you getting images, cache and recycle them.