Free Heap Memory When Loading Items Into A Listview.

I'm facing a big trouble with listview and items:

I'm loading a big amount of items (Text + Bitmap Image) into a Listview, i'm using an Adapter class which holds the items, and another class where the items are showed.

But when i start scrolling , the heap memory starts to grow and finally (after it reaches about 80mb acording to Xamaring Application Output), the application crashes.

I readed here ( http://stackoverflow.com/questions/13398367/how-to-solve-outofmemory-error-during-listview-scrolling ), that using a OnScrollListener, i can manage to disable the image load while it scrolls, and then load them when stop scrolling, but i can't set the listener the way it's exposed on the link above.

Can anyone please help me with this?

@jonp, @JamesMontemagno, @Cheesebaron

Best Answers

  • CheesebaronCheesebaron DK mod
    Accepted Answer

    The problem is that you are "holding" your images. You really need to stop that otherwise the memory footprint just keeps growing and growing until your app crashes.

    A good place to start is to read: http://docs.xamarin.com/recipes/android/resources/general/load_large_bitmaps_efficiently/

    And then start disposing your images right after you have assigned them to the ImageView (or as suggested in the article, using a using statement.

    If you want more specific help, you need to provide some more information on how you do things, i.e. post some reproducible code samples.

    Also I would appreciate not being called out in a thread like that if I didn't participate on my own will. Please stop that.

  • JamesMontemagnoJamesMontemagno US Xamurai
    Accepted Answer

    In addition to that there are some image caching frameworks out there for list views. I wrote one 2 years ago now that is part of my toolkit: https://github.com/jamesmontemagno/MonoDroidToolkit It isn't perfect, but has worked for me well.

Answers

  • CheesebaronCheesebaron DKInsider, University mod
    Accepted Answer

    The problem is that you are "holding" your images. You really need to stop that otherwise the memory footprint just keeps growing and growing until your app crashes.

    A good place to start is to read: http://docs.xamarin.com/recipes/android/resources/general/load_large_bitmaps_efficiently/

    And then start disposing your images right after you have assigned them to the ImageView (or as suggested in the article, using a using statement.

    If you want more specific help, you need to provide some more information on how you do things, i.e. post some reproducible code samples.

    Also I would appreciate not being called out in a thread like that if I didn't participate on my own will. Please stop that.

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai
    Accepted Answer

    In addition to that there are some image caching frameworks out there for list views. I wrote one 2 years ago now that is part of my toolkit: https://github.com/jamesmontemagno/MonoDroidToolkit It isn't perfect, but has worked for me well.

  • FraelReinosoFraelReinoso USMember ✭✭

    Well, first of all: Thanks for your fast response

    Now this is what i have:

    I have an adapter clas with some List for data related to each item (title, genre, coverPaht and duration).

    This is the adapter code:

    [Activity (Label = "VideoAdapter")]         
    public class VideoAdapter : BaseAdapter<String>
    {
        public static List<String> genre = new List<String> ();
        public static List<String> video = new List<String> ();
        public static List<String> cover = new List<String> ();
        public static List<String> duration = new List<string> ();
        Activity context;
        Android.Content.Res.Resources res;
        public Boolean ViewStates = false;
        public VideoAdapter(Activity context, Android.Content.Res.Resources res) : base()   {
            this.context = context;
            this.res = res;
        }
    
        public override long GetItemId(int position){
            return position;
        }
    
        public override String this[int position] {
            get { return ""; }
        }
    
        public override int Count {
    
            get { return ReadVideos.total; }
        }
    
        public override View GetView(int position, View convertView, ViewGroup parent){
    
            View view = convertView;
            view = context.LayoutInflater.Inflate(Resource.Layout.video_items, null);
    
            if (view == null) {
            view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
            }
    
            setView (view, position);
            return view;
        }
    
        public static void setView(View view, int position) {
            try {
                view.FindViewById<TextView>     (Resource.Id.vtitle).Text = (video[position]);
                view.FindViewById<TextView>     (Resource.Id.vartist).Text = (genre[position]);
                view.FindViewById<TextView>     (Resource.Id.vduration).Text = (duration[position]);
                view.FindViewById<TextView>     (Resource.Id.vtitle).Gravity = (GravityFlags.Center);
                view.FindViewById<TextView>     (Resource.Id.vartist).Gravity = (GravityFlags.Center);
                view.FindViewById<ImageView>    (Resource.Id.icon).SetImageURI(Android.Net.Uri.Parse(cover[position]));
            } catch (Exception) {}
        }
    
        public static void clearItems() {
    
            genre.Clear ();
            video.Clear ();
            cover.Clear ();
            duration.Clear ();
        }
    
        public static void addItem(String vid, String gen, String dur, String cov)  {
            float val = SongAdapter.msToMins (float.Parse (dur));
            int ent = (int.Parse (dur) / 60000);
            int hr, mn, se, ms;
            genre.Add (gen);
            video.Add (vid);
            cover.Add (cov);
            ms = int.Parse (dur);
            hr = (ms / 3600000);
            mn = (ms - (hr * 3600000)) / 60000;
            se = (ms - ((hr * 3600000) + (mn * 60000))) / 1000;
            ms = (ms - ((hr * 3600000) + (mn * 60000) + (se * 1000)));
            //duration.Add (dur + " MS");
            duration.Add ("0" + hr.ToString() + " Hrs " + ((mn <= 9)? "0" : "") + mn.ToString() + " Min " + ((se <= 9)? "0" : "") + se.ToString() + " Seg " + ms.ToString() + " MS");
        }
    }
    

    The reader class (ReadTracks) have some static List that i use to set the adapter to a fixed items count, i also can use one of the list in this class, but i need to update them from another process to make it dinamycal, it also have a updater method .

    If you need something more, just let me know, and again, thanks for your response.

  • FraelReinosoFraelReinoso USMember ✭✭

    Thanks @JamesMontemagno, i'll check it out and test it.

  • FraelReinosoFraelReinoso USMember ✭✭

    Yeah @Cheesebaron, it looks like the piece of code that crash my app.

    To give you a better idea: When my app starts, a thread in the Reader class reads all media files, then extract all the info i need and add them to the list adapter into the Adapter class, from the adapter, they're added to the listview on the MediaList class.

    I'm also thinking i need to kill the activity (clear class) when i leave it, because when i go back to main activity from MediaList and then return to MediaList, the items remains on the same position i leave, and i need to remove them from memory when i close the activity and reload them when i go back to list (Reload the class).

    I'm pretty sure that more than items causing me memory overflow, the opened classes also makes memory heap demand grow up.

    And sorry for the mention, but i want you to see the response for the question you made to me.

    Thanks for all.

  • FraelReinosoFraelReinoso USMember ✭✭
    edited January 2014

    Another useful data: I'm compressing the images and saving it to the sdcard, so the app just extract it once if the file does not exist, then every time it runs just load them from the sd card to avoid a long waiting time and extra resource consumption. The images ar saved at a 30/100 compression rate, so they're not so big at all (about 85KB the biggest one).

  • FraelReinosoFraelReinoso USMember ✭✭
    edited February 2014

    My problem is that each time i scroll over the ListView, it loads again it's content, and this makes the dalvik heap to grow untill it reaches the max value. I mean, it loads each item again, but just on memory, not on the list.

    How can i avoid loading the same items i loaded before ?

    Please somebody help me.

  • FraelReinosoFraelReinoso USMember ✭✭

    I found my problem, it was in the GetView method, i fixed it like this:

    public override View GetView(int position, View convertView, ViewGroup parent){
    
        View view = convertView;
    
        if (view == null) {
        view = context.LayoutInflater.Inflate(Resource.Layout.video_items, null);
        }
    
        setView (view, position);
        return view;
    }
    
Sign In or Register to comment.