Forum Xamarin.Android
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Dispose ImageView on GridView Custom Adapter OOM

Hello ! I'm trying to make a "guess the movie" like.
I've 2 tables in database store in my assets and copy, the first time, in the application.

I've a first page who is display all the categorie (movie, video games, tv shows etc...)
when I click on them, I open a new activity who is display a custrom gridview with all the images in the categories.

That's work 5 times, and after, that crash with this error :

Java.Lang.OutOfMemoryError: Failed to allocate a 5760012 byte allocation with 2642000 free bytes and 2MB until OOM

I've this code for my Custom Adapter :

public class CustomGridViewAdapter : BaseAdapter
    {
        private Context context;
        private List<Question> allQuestion = new List<Question>();
        private int[] allImages;

        public CustomGridViewAdapter(Context context, List<Question> allQuestion, int[] allImages)
        {
            this.context = context;
            this.allQuestion = allQuestion;
            this.allImages = allImages;
        }

        public override int Count
        {
            get
            {
                return allQuestion.Count;
            }
        }

        public override Java.Lang.Object GetItem(int position)
        {
            return null;
        }

        public override long GetItemId(int position)
        {
            return 0;
        }

        protected override void Dispose(bool disposing)
        {
              base.Dispose(disposing);
        }

        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            View view;
            LayoutInflater inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);

            if(convertView == null)
            {
                view = new View(context);
                view = inflater.Inflate(Resource.Layout.gridItem, null);
                ImageView txtGrid = view.FindViewById<ImageView>(Resource.Id.overlayGrid);
                ImageView imgGrid = view.FindViewById<ImageView>(Resource.Id.imgGrid);

                if(allQuestion[position].Done == 1)
                {
                    txtGrid.SetImageResource(Resource.Drawable.check);
                }else 
                if(allQuestion[position].Lock == 1)
                {
                    txtGrid.SetImageResource([email protected]);
                }
                imgGrid.SetImageResource(allImages[position]);
                imgGrid.Dispose();

            }
            else
            {
                view = (View)convertView;
            }

            return view;
        }
    }

and I call it here, in an other activity :

 protected override void OnResume()
        {
            base.OnResume();
            allQuestion = d.getAllQuestionsByCategorieId(categorieId);
            allImages = new int[allQuestion.Count];
            int count = 0;
            foreach (Question c in allQuestion)
            {
                int resImage = Resources.GetIdentifier(c.Img, "drawable", PackageName);
                allImages[count] = resImage;
                count += 1;
            }
            //allImages = null;
            adapter = new CustomGridViewAdapter(this, allQuestion, allImages);
            adapter.NotifyDataSetChanged();
            grid.Adapter = adapter;
            grid.ItemClick += Grid_ItemClick;
        }

Could you help me please !

Tagged:

Answers

  • SebastianSeidel.9226SebastianSeidel.9226 DEInsider, University ✭✭✭✭

    At a first glance it looks like you are preloading all the images and pass them to the adapter. In my opinion it is not a good way of doing it this way. A better way is to load single required image in GetView inside your adapter. The reason for this is, that only the images get loaded that should be displayed.

  • Thank you for your answer, that's done now, that's better, I've no more this message, but if i've a grid with lot of images, I'm affraid that's append again ! How could I free the memory ?

  • Oh sorry, I've this again this message "Java.Lang.OutOfMemoryError: Failed to allocate a 5760012 byte allocation with 1895712 free bytes and 1851KB until OOM"

  • SebastianSeidel.9226SebastianSeidel.9226 DEInsider, University ✭✭✭✭

    Next step would be to optimize your GetView method. Read about the ViewHolder pattern and implement this in your GetView method.

  • Ok now, I've this, but that doesn't work....

     public override View GetView(int position, View convertView, ViewGroup parent)
            {
                MyViewHolder holder = new MyViewHolder();
                var view = convertView;
                if (view != null)
                    holder = view.Tag as MyViewHolder;
    
                if (view == null)
                {
                    view = activity.LayoutInflater.Inflate(Resource.Layout.gridItem, null);
                }
    
                if (holder == null)
                {
                    holder = new MyViewHolder();
                    holder.Overlay = view.FindViewById<ImageView>(Resource.Id.overlayGrid);
                    holder.Image = view.FindViewById<ImageView>(Resource.Id.imgGrid);
                    view.Tag = holder;
                }
    
                if (allQuestion[position].Done == 1)
                {
                    holder.Overlay.SetImageResource(Resource.Drawable.check);
                }
                else
                if (allQuestion[position].Lock == 1)
                {
                    holder.Overlay.SetImageResource([email protected]);
                }
    
                int resImage = activity.Resources.GetIdentifier(allQuestion[position].Img, "drawable", activity.PackageName);
                holder.Image.SetImageResource(resImage);
    
                return view;
            }
    
    
            private class MyViewHolder : Java.Lang.Object
            {
                public ImageView Overlay { get; set; }
                public ImageView Image { get; set; }
            }
    
  • SebastianSeidel.9226SebastianSeidel.9226 DEInsider, University ✭✭✭✭

    What is not working?

  • Ok I fix it ! It was because the variable holder was not null...

    But, now the view holder works, but I've still the memory error...

  • SebastianSeidel.9226SebastianSeidel.9226 DEInsider, University ✭✭✭✭

    Ok now comes the fun part. You need to use a profiler to see where the memory leak is. But before you do so, what are the dimensions of the images?

  • Yeah I thinks It's because the images are too big, the dimensions are 600x600

  • I already try this : https://developer.xamarin.com/recipes/android/resources/general/load_large_bitmaps_efficiently/

    But I can't use it in the "GetView" of my gridViewAdapter because I need to make it async...

Sign In or Register to comment.