2

I'm facing an OutOfMemoryException when I try to download images (that can be large images). I'm working with Xamarin.Android and a PCL for cross-platform actions.

I want to do an image slideshow. I have a limited number of imageviews superposed on my layout. When I have swipe out all images, I reload new images in those imageviews. I've made a simple project with just the refreshing part of the mechanism.

Please, be nice, I'm a beginner in Android and Xamarin, here is the code :

MainActivity.cs:

[Activity(Label = "App1", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        private string[] urls = {
                                    "http://momotaros.fr/wp-content/uploads/2014/01/One-Piece-5.png",
                                    "http://www.infinite-rpg.com/wp-content/uploads/2014/09/Equipage.png",
                                    "http://ekladata.com/ke33JLPQ2tTW1mTHCvPJBrKCPOE.jpg",
                                    "http://ekladata.com/P2Q1mQuYTDbfEZnwb3f3IBsXoGM.png",
                                    "http://ekladata.com/9QUE66iKU9uGUPVUdFmPF_ZIkK8.png"
                                };
        protected override void OnCreate(Bundle bundle)
        {
            BlobCache.ApplicationName = "App1";
            BlobCache.EnsureInitialized();
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            Button button = FindViewById<Button>(Resource.Id.MyButton);
            ImageView image = FindViewById<ImageView>(Resource.Id.imageView1);
            ImageView image2 = FindViewById<ImageView>(Resource.Id.imageView2);
            ImageView image3 = FindViewById<ImageView>(Resource.Id.imageView3);
            ImageView image4 = FindViewById<ImageView>(Resource.Id.imageView4);
            ImageView image5 = FindViewById<ImageView>(Resource.Id.imageView5);
            button.Click += (sender, e) =>
            {
                image.SetImageURL(urls[0]);
                image2.SetImageURL(urls[1]);
                image3.SetImageURL(urls[2]);
                image4.SetImageURL(urls[3]);
                image5.SetImageURL(urls[4]);
            };
        }
    }
    public static class BitmapExtensions
    {
        public static async void SetImageURL(this ImageView imageView, string url)
        {
            IBitmap bmp = await App1.Shared.ImageDownloadService.DownloadImage(url, imageView.Width, imageView.Height);
            if (bmp != null)
            {
                imageView.SetImageDrawable(bmp.ToNative());
            }
        }
    }

ImageDownloadService.cs (in a PCL):

public class ImageDownloadService
    {
        public static async Task<Splat.IBitmap> DownloadImage(string url, float desiredWidth, float desiredHeight)
        {
            return await BlobCache.LocalMachine.LoadImageFromUrl(url,false, desiredHeight:desiredHeight,desiredWidth: desiredWidth);
        }
    }

When the button is clicked the 1st time it's OK, images are downloaded (even if I found memory usage a little bit high in DDMS).

But for next clicks, watching over memory usage, it increases like hell.

I was thinking that previous bitmaps in memory are not disposed when I set a new image in that imageView, kind of a strong reference somewhere but I can't find out where it is if it's that.

I would appreciate your help for this problem or any techniques for debugging memory usage like something to track where an object is created and where it's is destroy.

Thanks for your time reading this post, and I hope you can help me out.

Jordan Gueguen
  • 128
  • 3
  • 11
  • Are you aware that Disposing an object and forcing garbage collection are not the same thing? – toadflakz Feb 19 '15 at 11:13
  • @toadflakz Well I know that it's different but I don't know in which way it's different. – Jordan Gueguen Feb 19 '15 at 11:42
  • Forcing garbage collection actually releases the memory, while disposing only marks the memory as releasable. Try calling `GC.Collect()` once you've assigned a new image to release the old image's memory. – toadflakz Feb 19 '15 at 14:19
  • When loading images it is recommended to check imagsize and resolution, bcz device may not be able to handle large image, sub sampling the image bitmap may solve this problem. this might be helpful http://appliedcodelog.blogspot.in/2015/07/avoiding-imagebitmap.html – Suchith Jul 24 '15 at 05:40

1 Answers1

2

Inside your BitmapExtensions you should probably just dispose of the Bitmap immediately after you have assigned it to the ImageView as you are not going to use it for anything else afterwards.

public static class BitmapExtensions
{
    public static async void SetImageURL(this ImageView imageView, string url)
    {
        using (IBitmap bmp = await App1.Shared.ImageDownloadService.DownloadImage(url, imageView.Width, imageView.Height))
        {
            if (bmp != null)
            {
                using(var nativeBmp = bmp.ToNative())
                    imageView.SetImageDrawable(nativeBmp);
            }
        }
    }
}

You should also make sure that the DownloadImage method catches all Exceptions thrown inside, otherwise you will have a bad time if one occurs.

Calling GC.Collect should not be necessary, as you are then forcing the GC to collect at that moment, possibly making the application unresponsive while it is doing its thing. Just be sure to dispose of all your references and you should be golden.

Cheesebaron
  • 24,131
  • 15
  • 66
  • 118
  • Thanks @Cheesebaron, it was the problem for this problem. Setting bmp to null at the end or a using statement solve the problem. Unfortunately, I thought it was the same problem on my other project but it was not. But I have finally find out what was wrong on the other one too. – Jordan Gueguen Feb 20 '15 at 08:39
  • @Cheesebaron : I have listview and View Pager as Item (having 5+ images) of Listview. All the images having width and height same screen width. I'm loading images with Picasso. (Also tried UIL). But it still gives me OOM. – Sagar Panwala Apr 06 '16 at 12:15