3

I am having an activity with a listview in it. The listview is populated from a large XML pulled from a server. My scenario usually is to have a SAX parser, parse the XML and return a Vector(or similar structure) with the parsed data. The problem is that the xml is too big and the Vector has too many elements which causes out of memory errors. Also the xml has links to images which are being downloaded and this makes the situation even worse.

How should I manage the memory in such cases? I was thinking if I could make the images load only when the user scrolls to a given row in the list view.

gop
  • 2,150
  • 5
  • 26
  • 54
  • i think first parse the XMl and parsed data store in array or arraylist.. and then bind data in listview.. and for image use lazyloading.. – Sanket Kachhela Jul 23 '12 at 07:44
  • I implemented a CursorAdapter that loads the image thumbnail from a file during bindView (the file name is cached in the database). – Sparky Jul 23 '12 at 07:48
  • 1
    Using the BaseAdapter you can set the images to lazy load. (About the large xml, how large? You mean you get out of memory from having a list with too many objects??) – Nuno Gonçalves Jul 23 '12 at 07:49
  • @Nuno Gonçalves first I thought that the objects were too many but then I realized that I am loading way too many images. How should I lazy load the images? I mean, I can store the image url but how to know when to load the ones currently should be on screen? – gop Jul 23 '12 at 08:00
  • I didn't read all answers, but I'm almost sure there's already a valid answer there. Anyway, on the getView() method, just create a new thread to load the image on the web, from the link you have. :) There are examples out there, if the answers didn't do the trick. – Nuno Gonçalves Jul 23 '12 at 09:03

3 Answers3

2

The problem of out of memory exceptions is the loading of the images. The Adapter shouldn't have a problem in populating itself in a ListView.

In a business application I am loading a ListView with 9000 records with 2 TextViews in each row. So I guess the size of the data should not be a problem.

Use an AsyncTask to fetch the data from the server. Parse and populate the adapter in the doInBackground() method and then in onPostExecute() set the Adapter in the ListView. At first try not to include any of the images so as to make sure that data size is not a problem.

If the ListView loads correctly then try and find a way to populate the images. There are multiple ways in doing this operation. If the images are the same over and over again you should try to "cache in memory" implementation, otherwise perhaps "lazyloading" from a background thread should do the job just fine.

EDIT:

this is a very good post/tutorial on how to lazyload images from a background thread. It is "almost" the same technique like the android market uses when you are viewing all the applications in a GridView, when at first there are some grey icons and then suddenly the image of the application appear. This is done from a background thread, when the exact row is visible to the user then the background thread fetches it from the internet, populates the list and it caches it in memory so as to be available again in no time. Lazy load of images in ListView

Community
  • 1
  • 1
10s
  • 1,699
  • 1
  • 12
  • 14
  • you are absolutely correct. What is the lazy load trick? How to know when to load a given image from its url? I want to load it when its row in the listview is visible i.e. when the user scrolls to it. – gop Jul 23 '12 at 08:01
  • thanks again I just tried something - I created a custom adapter and implemented getView where I have an ImageView. I just started an AsyncTask whenever the getView is invoked and it works well and scrolling is smooth, the problem however is that when I scroll up again the images are loaded once again. It would be better if the listview caches them somehow. Is this possible? I don't think it will be a good idea to use my data structure to keep the loaded bitmaps because I will have memory problems again. – gop Jul 23 '12 at 08:30
  • 1
    check the above link. When it fetches the image, it has a little bit of code somewhere that caches the image in a `map` and when it is time to refetch it again the program first checks the `map` and if does not exist then tries to make the async call. However, this is the crucial part, how many different images are you trying to populate? – 10s Jul 23 '12 at 08:41
  • This is the problem I have to populate about 400-500 images, caching them all will cause memory problems again. Maybe a good idea is to keep some upper limit of 10-15 images and keep them stored in a Queue-like structure(simple array maybe) and whenever the limit is reached discard the oldest and add the new one, this way I can keep the last 10 the user has seen. – gop Jul 23 '12 at 08:49
  • 1
    Yes this would cause an out of memory exception. Your optimized solution is very good and should be enough. Although there are implementations for the caching of the images in disk or in memory. The basic principle is that you store the drawable as a `File` and then you load it from there. This is a different technique that is used in image galleries and these kind of apps. A very good tutorial can be found in the docs: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html – 10s Jul 23 '12 at 08:59
  • @gosho_ot_pochivka You can use weak references like in my answer below – bughi Jul 23 '12 at 09:00
  • weak references are a little bit outdated. In the docs I posted there is a note that says that weak references should not be used because garbage collector is much more aggressive in api 9 and above. `Note: In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache, however this is not recommended` – 10s Jul 23 '12 at 09:07
0

Use service to pull XML from server into database. Your activity than lazily loads data from database like you can see in Google Play. See CWAC EndlessAdapter. See also Universal Image Loader for Android to fill your listview with images.

biegleux
  • 13,179
  • 11
  • 45
  • 52
0

The OutOfMemoryException is most likely caused by the images.

Lets say you want to show a list of hotels with pictures. You get the hotel info in XML format and save them in a List (or similar structure). Your hotel class could have a member like URL pictureURL and a member like WeakReference<Drawable> picture that will be null until needed.

You can make your list adapter to start a AsyncTask to download a image in the getView() method (if picture point to null) and set it to both the ImageView in your list and Hotel.picture.

Because Hotel.picture is a weak reference, the memory will be freed when the image goes off screen as the ImageViews get recycled

Your getView method might contain something like this:

private class MyCustomAdapter extends BaseAdapter {
    ...
    List<Hotel> hotelList;
    ...
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ...
        Hotel hotel = hotelList.get(position);
        ImageView ivHotelPic = //your image view from the list (child of 'view')
        ...
        if (hotel.picture == null || hotel.picture.get() == null){
            new DownloadImageTask(hotel,ivHotelPic).execute(hotel.pictureURL)
        }
        else{
            ivHotelPic.setImageDrawable( hotel.picture.get() );
        }

        return view; 
    }
}

private class DownloadImageTask extends AsyncTask<URL, Integer, Drawable> {
    Hotel hotel;
    ImageView imageView;

    DownloadImageTask(Hotel hotel,ImageView imageView){
        this.hotel = hotel;
        this.imageView = imageView; 
    }       

    protected Drawable doInBackground(URL... urls) {
         assert urls.length == 1;

         Drawable image = //download image from urls[0] (or hotel.pictureURL);

         return image;
    }

    protected void onPostExecute(Drawable result) {
        imageView.setImageDrawable(result);
        hotel.picture = new WeakReference<Drawable>(result);
    }
}

You could also use a regular reference for your images if you want to keep them until the activity closes, but this might cause OutOfMemory when too many images were downloaded.

bughi
  • 1,838
  • 1
  • 13
  • 24