I am bulding up a grid of images for an app I'm building. It works like so:
Build up a list of Image IDs, which I must query using a different content provider each (these are images from MMS threads)
Create new activity, which hosts an ImageGridFragment. This fragment has a custom adapter that takes the grid of images, and loads each one as a bitmap asynchronously.
After images are loaded, they are cached in an LRU cache so I don't need to run unnecessary computation
So far, everything works quite well. However, I would like to pre-buffer images so that when the user scrolls down, s/he doesn't have to wait for images to load. They should already be loaded. The stock Android Gallery accomplishes. I've had a look at the source, but think there must be a more straightforward way.
To answer members' questions
Images are loaded one by one using the content://mms/part/xxx, where xxx is the ID of an image. These are MMS images, and to my knowledge, cannot be loaded as a batch process (though, maybe I'm wrong). I use a content provider in an AsyncTask to load each image
I've tried the following:
Pre buffer 30 images or so right when the fragment is created. This is not ideal because the massive I/O request, actually prevents the on-screen images from loading quickly (but the buffering does work well!)
Detect when the requested view to load is at the very bottom-right hand corner of the screen, which could work, but then would fail in the case that the GridView takes up only part of the screen. It also seems like there should be a cleaner way to do this
Thought about, but did not try, an OnScrollListener, but this will not pre-buffer images until I start scrolling, which is not ideal
So, my questions are:
Is there a good way to detect when the last GridView item is requested to load? I found that the GridView.getlastvisibleposition() method is not useful here, because it is actually returning the last element for which Adapter.getView() has been called for. If I can do this accurately, I can launch the buffer request at that time
Is there a better way to do this?
you can do right this
if(GridView.getlastvisibleposition() = mAdapter.count()-1)
how you load the images?
is it from URL or from sdcard?
are you using a image loader library?
Related
I'm using Android-Universal-Image-Loader in a ListView of mine and I'm trying to find the best solution to following:
using resetViewBeforeLoading is necessary or else I get the same image in my ConvertViews, but this causes jitter, unless..
I use PauseOnScrollListener which is otherwise great, except that it shows a blank in some ConvertViews even for images that are already downloaded (I'm using memory and disk caches), so it's confusing to the user who sees a blank for an image they saw only 2 swipes ago
So it seems that I can't get an instant image load (for already-downloaded images) on scroll without jitter, even for images in memory, is this about right? Is there a better or more standard way to do this? (Vertical list-view showing screen-width images, sort of like the Instagram app, which does it buttery-smooth)
Otherwise, is there a way to lengthen the number of convertViews in my ListView to prevent unnecessarily aggressive re-use?
Thanks in advance
I'm making an ListView in my app which over time could contain hundreds of items. Are there any "best" methods of loading lots of data?
My idea is to load it in chunks (say 10-20 items). Load the first chunk, then when the user is about halfway through scrolling, load the next chunk, add it to the bottom of the list (and make sure the list scroll offset doesn't jump about).
Some other ideas I had just didn't like so much were accepting the cost of a large http call and load all the data at once, but just load it in chunks as they scroll, or maybe add a "Next x items" button at the bottom, or loading all the items into the list at once and having one large list I don't need to keep track of.
I personally like my original idea, I was just wondering if there is a preferred method or doing this, and if there are any performance issues I could have.
The data in question will be a JSON string, and each item will display some title text, a date, the author of the item, and an image which will be downloaded using the Picasso library.
Your initial idea is my preferred approach because it works very well in most situations.
The second one may work well, but the problem is, the "large" data concept is relative across devices. For powerful devices you may load 2000 items at once, but it will kill older, slower phones. Also, if you're loading 2000 items when the use case of that ListView is to choose one in the first 100, you are wasting bandwith.
The first approach is very scalable: You really don't care if there are 5 items or 50 million, you just load chunks as the user consumes them. The memory usage is consistent. Coupled with ListView's view recycling, this will have a small memory footprint.
To say something positive about the second approach: Maybe in a use case when the ListView always has the same data, and it rarely changes, for example, an image library, you may want to load all the data at application start and cache it, so you never have to do network requests while the user is using the app. If the data size is not huge, I'd go for this second approach. But always having in mind that there's a critical size after which you will need to page!
Basically you can load all items once and show them all (if you will use ViewHolder pattern and lazy image loading using Picasso - everything should be ok). If you have some business logic which force you to show data by pages - you can follow the way that you described first.
If you will show all data - you can add search by list for better UX(you also can do it when you have pages but it will be more tricky).
Some time ago I wrote an article on similar topic:
http://developer.samsung.com/android/technical-docs/Batch-loading-list
The article comes also with a small library of common classes.
Maybe you will find it helpful.
Cheers,
Hi guys i am new to android and i posted a question a week ago in this link which basically stated that i was getting a java.lang.outofmemory error when i was using a lot of different backgrounds for my activities.
why am I getting errors when I use different backgrounds in xml
So as a new developer I have searched and searched for a solution as to how to clear the memory as i go from activity but none have been clear or precise. Then i stumbled across this site http://androidactivity.wordpress.com/2011/09/24/solution-for-outofmemoryerror-bitmap-size-exceeds-vm-budget/
which described exactly what i was going through except they use 10 activities and i am only using 4. However when i implemented his code it my project i ended up with null pointer exceptions and after fiddling with his code I ended up back were i started with the same out of memory error.
So can anybody direct me to someone who can show me how to have as many backgrounds as i want with out running out of memory. Or does android as great as it is does not let you simply use more than a certain amount of backgrounds? help?
It's not that there is a limit on the amount of backgrounds, but each background image you load is a loaded into memory as a bitmap and held there until the activity is destroyed. If you are opening multiple activities one after another, each background image will need to be held in memory and so eventually you will get an out of memory exception.
If you set a large background image, you will also experience some blocking on the ui thread, while the image is loaded into memory.
One way around this that worked for me was to use an imageloader. This decodes the image off the ui thread, caches it on disk, loads it into memory and if memory is running low, will clear an image from memory and fallback to the disk cache. You may get a slight delay/fade in as the image is loaded but this is not so bad visually, and when loaded once, will load straight away if you go back to that activity.
Check out Picaso Picasso which is really easy to implement and a great api or Universal Image Loader.
My layouts were all RelativeLayouts and the first child (will be behind all other views) was an ImageView with scaleType centercrop and width and height set to match_parent. When each activity loads (onCreate), just grab a reference to the imageview in your layout and set the required background image using your ImageLoader of choice.
The other option is to have multiple copies of your background image in your resources, with each one resized to perfectly fit your resolutions of choice (drawable-mdpi/-hdpi/-xhdpi etc). This way, you ensure you are never loading images that are way bigger than you need to be displayed and your app will be more forgiving in terms of memory consumption.
So I have a listview with an image and some text hooked by a custom array adapter. The problem I am facing is that the image is still loading a little bit too slow for my liking. I've watched the google tech talk and attempted to optimize my list by resusing the view by the convertView (if it's null then inflate, if not, resuse). I've also spawned AsyncTasks to load the bitmap with BitmapFactory.Options inSampleSize set to a power of 2, since the image is relatively small. I've also used the ViewHolder pattern to reduce findViewById calls.
However, even with this, when I scroll through my list, it is very noticeable how the convertViews are being reused because the image constantly gets updated as I scroll up and down.
What I noticed in the stock Android photo gallery is, with the hundreds of photos that I have, when I scroll through quickly, the photos initially load slowly, but then get cached. The amount cached is MUCH more than what the screen is capable of showing, so as I scroll, initially the photos load seamlessly until I scroll pass the amount of photos cached, and then the reload of photos is noticeable again.
Is there a way to do this with the ArrayAdapter? Basically, store more than the 9 views within my listview (What my screen is capable of showing) for the purposes of when a user scrolls down quickly, the user would have to scroll down a lot before the convertView is reused and thus the noticeable image reloads?
Thanks in advanced!
Load the images in the background, using multiple parallel threads.
For example: Sending Operations to Multiple Threads; the sample app downloads thumbnails from the Picasa Featured Photos RSS feed. Hope this helps.
In my Android App I have a listview containing 30 rows, and each row consists of several textviews of which one is spannable and sometimes contains a lot of formatted text and images.
Those images are loaded from the web asynchroneously: A placeholder is displayed until the image has been downloaded is then replaced by the image.
Unfortunately, the rows of the listview are loaded when I scroll over them. This makes the whole thing very slow. Also, those images are loaded from the web again and again, whenever I scroll over the row.
How can I turn it off, that the ListView rows are loaded when I scroll over them? They should be loaded once when I start the activity and never again.
Best regards and thanks in advance,
Jan Oliver
When you do a lazy-loading ListView, is because you want to speed it up your app. Turn it off is not the best solution. So, what you can do is implementing a basic cache system in order to avoid downloading and setting the ImageView again and again.
The easiest way to do so is implementing a HashMap with URLs as keys and Bitmaps as values. Something like this:
Map cache = new HashMap();
// then, on your lazy loader
Bitmap image = cache.get(urlOfTheImage);
if( image == null ){
// download and decode the image as normal,
// then assign the decoded bitmap to
// the 'image' variable
cache.put(image);
}
imageView.setImageBitmap(image);
If those images will be the same always, meaning that each time you open the app the same images will be downloaded, then you better save those images in the filesystem and use them from there.
On the other hand, if the images tend to change, you could implement some interesting stuff: use SoftReferences. There's an explanation in this video. This can also be used if you are loading images from the filesystem.
Edit
With regards to your comment, I highly recommend you watching the video I posted. It's one hour long, but it really worths the effort. When using an adapter, checking if the convertView is null is just a simple way to improve performance, though there are some other techniques that will improve your app even more. Also, if you had have problems while using that trick, is because you are probably implementing it the wrong way. Remember: even if you don't re-inflate the views, you do have to set the value of each one of the children views, otherwise you will experience some problems.
If you can, start with an Image Array full of the "placeholder images", then download the images in to an Array firing an AsyncTask during on Create. During row view building just refer to the array. That way if it has the new image it will load it, if not it will get the placeholder.
If you have a lot of data its gonna get real slow and be a crappy expirience for the user.
Create a list of objects that represent each row. Create a loader as a background thread that updates the objects as it loads the data. Your list view will draw data from the objects.
(Not a good idea if you have hundreds of rows and a huge amount of data in each row - in that case, you should only load data within a few rows of the currently active row and have some sort of MRU cache).