Out of memory on Android with listview having an image in it

DevinRoseDevinRose USMember ✭✭
edited August 2014 in Xamarin.Forms

I have discovered that Android can run out of memory quite quickly with a listview that has an image in its itemtemplate, if that image has a large width and height.

The file size of the image does not matter, just the width/height of it. My image is a 1280x337, 3KB png, and with just five to ten items in my listview, it will crash when you scroll it.

    public OtherPage ()
    {
        Title = "Other Page";

        BackgroundColor = Color.White;

        var settings = new ListView ();
        settings.RowHeight = 120;

        List<string> items = new List<string> ();
        for (int i = 0; i < 8; i++) {
            items.Add ("Item " + i);
        }
        settings.ItemsSource = items;

        settings.ItemTemplate = new DataTemplate (() => {

            Image gradientOverlay = new Image()
            {
                Source = "colorgradient.png",
                Aspect = Aspect.Fill
            };

            Grid screenGrid = new Grid () {
                VerticalOptions = LayoutOptions.FillAndExpand,
                HorizontalOptions = LayoutOptions.StartAndExpand,
                RowDefinitions = 
                {
                    new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }
                },
                ColumnDefinitions = 
                {
                    new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
                }
                };

            screenGrid.Children.Add(gradientOverlay, 0, 0);

            return new ViewCell { 
                View = screenGrid,
            };
        });

        Content = settings;

    }

Bug filed here: https://bugzilla.xamarin.com/show_bug.cgi?id=22510

Note: image is attached below but image is mostly white so is hard to see.

Posts

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I feel like your image is just trolling us... :)

  • DevinRoseDevinRose USMember ✭✭

    Doh! After I edited the post the image attachment disappeared. Hmm, re-added it so now it should be invisibly there but for reals this time.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    There was an image before (which just looked like a big white rectangle). Now there is no image at all. You're a master troll, Devin!

  • MihaMarkicMihaMarkic SI ✭✭✭✭
    edited August 2014

    I tried but can't repro even with 78 items. Can you upload a project? What Xamarin (Forms) are you using? I also see that Dalvik GC is working hard to free and allocate memory.

  • DevinRoseDevinRose USMember ✭✭

    Doh you're right the image is gone again. I will see if I can attach it here.

    Miha, I will upload the project BUT since you can reproduce it I wonder if it is device specific. This is when the app is running on my Samsung Galaxy S5. And I also see the Dalvik GC working hard on memory, but eventually it gives up the ghost.

  • DevinRoseDevinRose USMember ✭✭
    • meant to say "since you CAN'T reproduce it" up above.

    Miha, I'm using the latest of all packages I think. I am attaching the project. Do let me know what Android device or emulator you are running it on. Thanks!

  • MihaMarkicMihaMarkic SI ✭✭✭✭

    Can't crash it, tried on Genymotion and Galaxy Nexus (old) with Debug version from Visual Studio. Albeit on Nexus it was complaining that
    W/OpenGLRenderer( 2740): Bitmap too large to be uploaded into a texture (2560x674, max=2048x2048).
    Not sure why it did try to make it 2x larger on each axis. Once I've made it 1024 x something it worked without a crash.

    I am on Xamarin beta channel BTW.

    BTW2: The relase version doesn't work since linker complains.

  • DevinRoseDevinRose USMember ✭✭

    Good data points to have Miha. I haven't updated to the beta yet but will try it when it releases. Thanks man.

  • PaulPCEPaulPCE USMember ✭✭

    I posted a similar bug that outlines issues with Android list item recycling, and the (probably related) issues of OutOfMemory errors here. I've reproduced this with @jamesmontemagno's Monkeys sample on several different Android devices. Sounds like this might be fixed on the beta channel already - great news, I'll have to check it out!

  • Steven123Steven123 GBMember ✭✭

    Has there been any progress on this? Am looking at workarounds, but they are messy, be good to hear how others have dealt with it.

  • PeterAxelsonPeterAxelson USMember

    Yes, Android list item recycling, and the (probably related) issues of OutOfMemory errors are an issue for me as well. There are no current updates on the bug reports. Does anybody actually read these old posts?

  • MihaMarkicMihaMarkic SI ✭✭✭✭

    Actually I've given up on ListViews a while ago and switched to implementation through custom renderers as ListViews weren't/aren't flexible enough and had both perf and out of memory problems. Haven't tried latest implementations though.

  • NielsvanAmstelNielsvanAmstel NLMember ✭✭
    edited March 2015

    I'm having memory problems even without images in a list of 50 items with just text. When I scroll down and scroll back up, it dies on me. This is on a Samsung Galaxy S3.

    EDIT:
    My problems seem to be coming from the Frame element I've been using in the DataTemplate. I had a Frame around my StackLayout to apply padding, which I guess didn't bother to try to do on the StackLayout first. Removing the Frame and adding the padding to the StackLayout fixed it for me.

  • Steven123Steven123 GBMember ✭✭

    If you can go native on the Listview then following the D path on this:
    http://developer.xamarin.com/samples/xamarin-forms/WorkingWithListviewNative/.
    is showing encouraging results.
    Trying up to 1,960 images ( resized down ) through the native list view is so far giving stable readings on the Android Device Monitor.

  • My work around was simple... I had out of memory problems with loading just few PNGs and resizing the native image helped... For example I had a simple PNG file about 27k in file size but its inner dimension was 1080x300... And I assume that this was translating to a large memory footprint when being displayed...

    So I simply changed the native resolution of the file to something like 320px wide... (basically giving up on hi res version)... This worked perfectly...

    I am assuming that Xamarin will allocate RAM based on the native image size... Something like that??? Hmm...

    Did not have issues with I used native controls and PNGs...

    So for now that is my fix... Trade resolution for memory...

  • batmacibatmaci DEMember ✭✭✭✭✭

    @PhilipOchu said:
    Fun - Had similar issue just this last week - what I found out the hard way is the drawable folders in Android really do matter - your image is likely too large.

    If you drop an image in the default drawable folder only and say your device is classified as xhdpi. Android sees it does not have an image for your device and takes the image in the default drawable folder and then tries to scale it up to support your device. A drawable from default (mdpi) to xhdpi would be scaled up x2 - which would quickly run your device out of memory.

    If you do not want Android to scale up your image you can put it in the drawable-nodpi folder - regardless the image you provided seems still too large (not size on disk) but size in memory which can generally be calculated as 4 bytes x width in pixels x width in height.

    Thanks for saving lives but how do you do that- "device is classified as xhdpi." ?

  • PhilipOchuPhilipOchu USBeta, University ✭✭✭

    I am not sure if there is a programmatic way to identify a device's density (I assume there is).

    My approach was simply to have identical images but with different text on them (eg text was ldpi, mdpi, hdpi, xhdpi,xxhdpi) and put each of the images in the respective folders. Then I launched my test device and saw which image was displayed.

    There are some websites that attempt to catalog the different android devices:
    http://qualitytestingtips.blogspot.com/2013/08/ldpi-mdpi-hdpi-xhdpi-xxhdpi.html
    http://blog.blundellapps.co.uk/list-of-android-devices-with-pixel-density-buckets/

    Hope this helps

  • batmacibatmaci DEMember ✭✭✭✭✭

    @PhilipOchu said:
    I am not sure if there is a programmatic way to identify a device's density (I assume there is).

    My approach was simply to have identical images but with different text on them (eg text was ldpi, mdpi, hdpi, xhdpi,xxhdpi) and put each of the images in the respective folders. Then I launched my test device and saw which image was displayed.

    There are some websites that attempt to catalog the different android devices:
    http://qualitytestingtips.blogspot.com/2013/08/ldpi-mdpi-hdpi-xhdpi-xxhdpi.html
    http://blog.blundellapps.co.uk/list-of-android-devices-with-pixel-density-buckets/

    Hope this helps

    my confusion is the following. I am testing my app on sony z3, according to the list, sony z3 should run images as xxhdpi. What does that exactly mean?
    If I have images 1280 × 720 in the xxhdpi folder, those images will not be scaled up by Android?
    What if I have images already large enough as xxhdpi but by mistake I had copied them only in drawable(mdpi) folder. what is the behavior of Android?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    The reason that there are different folders for different DPIs is so that when the app displays those images they will appear roughly the same physical size. Consider what would happen if there was only one folder for images. Let's say you create an image that's 100x100, and on one device that shows up as 1cm across. On a display with twice the density the same image would show up as 0.5cm across. It would look too small.

    In order to avoid that you put that image in a folder that tells the OS what DPI you designed it for. If you designed it for an hdpi device then you put it in an hdpi folder. Then when it's displayed on a device with twice the pixel density as an hdpi device then Android will scale it up twice as big automatically.

    There's nothing wrong with that. You can't always create images for every single DPI possibility (we typically supported 3). The problem comes when you put an image in the wrong folder. If you design your image for an xhdpi screen but you put it in an mdpi folder then Android will scale up an image that was already the correct size to be way bigger, and then it will likely have to shrink it back down for display. That's a huge waste of memory, and can lead to running out of memory.

    So the fix is to be sure to put the image in the right folder. It should make no difference (in terms of memory) which DPI you design it for as long as you put it in the folder corresponding to the correct DPI.

  • DanielLDanielL PLInsider ✭✭✭✭
    edited August 2016

    @DevinRose Xamarin.Forms doesn't reuse bitmaps with the same source. It means that for every image, you get a new instance of Android Bitmap. You can solve it with CachedImage from FFImageLoading (https://github.com/luberda-molinet/FFImageLoading), it reuses bitmaps by default. So for colorgradient.png source, only one Bitmap is ever created and shared across all listview items. It's API compatible with Xamarin.Forms Image.

  • batmacibatmaci DEMember ✭✭✭✭✭

    @DanielL said:
    @DevinRose Xamarin.Forms doesn't reuse bitmaps with the same source. It means that for every image, you get a new instance of Android Bitmap. You can solve it with CachedImage from FFImageLoading (https://github.com/luberda-molinet/FFImageLoading), it reuses bitmaps by default. So for colorgradient.png source, only one Bitmap is ever created and shared across all listview items. It's API compatible with Xamarin.Forms Image.

    hi @DanielL ,

    I had similar problem and once it replacing FFImageLoading, memory usage dropped significantly. does it apply only xaml or even if I define as new FFImageLoading.Forms.CachedImage(), it will not create new CachedImage if it is already there. how does it identify ? should it have name as id? without name will create a new image? with code below, i am not naming my image, does it mean I am creating a new instance?

    relativeLayout.Children.Add(new FFImageLoading.Forms.CachedImage()
    {

  • DanielLDanielL PLInsider ✭✭✭✭
    edited December 2016

    @batmaci It will try to reuse bitmaps even between different instances of CachedImage. By default it uses filename / file url as a cache key (with some additional metadata added as downsample / transformations) parameters . You can change this behaviour by using custom cache keys with a CustomCacheKeyFactory property.

  • ebetzlerebetzler USMember ✭✭

    @PhilipOchu said:
    Fun - Had similar issue just this last week - what I found out the hard way is the drawable folders in Android really do matter - your image is likely too large.

    If you drop an image in the default drawable folder only and say your device is classified as xhdpi. Android sees it does not have an image for your device and takes the image in the default drawable folder and then tries to scale it up to support your device. A drawable from default (mdpi) to xhdpi would be scaled up x2 - which would quickly run your device out of memory.

    If you do not want Android to scale up your image you can put it in the drawable-nodpi folder - regardless the image you provided seems still too large (not size on disk) but size in memory which can generally be calculated as 4 bytes x width in pixels x width in height.

    Philip- Thank you. You are my hero!

Sign In or Register to comment.