Xamarin.Forms ListView OutOfMemoryError在Android上出现异常

本文关键字:异常 Android Forms ListView OutOfMemoryError Xamarin | 更新日期: 2023-09-27 18:21:48

有人尝试过使用包含Image视图的ItemTemplate的Xamarin.Forms Listview吗?现在,当ListView包含大约20行或更多行时会发生什么?

至于我,我有一个大小约为4K的.png文件加载到图像视图中。在应用程序因OutOfMemoryError而崩溃之前,最多显示9-12行。在android Manifest中请求一个大堆后,该应用程序在60-70行后崩溃。

我知道Xamarin正在推广使用BitmapFactory类来缩小位图,但这不适用于Xamarin Forms图像视图。

我正在尝试摆弄ImageRenderer的一个子类,看看我是否可以添加BitmapFactory.Options属性,以及这是否能解决问题。

此外,我可能需要检查Xamarin.Forms是否在ViewCell滚动屏幕后处理(回收)了包含的位图。

在踏上这段旅程之前,我非常希望得到任何可以让这一过程变得更容易的意见,或者一个认为这一过程没有必要的更简单的解决方案。

展望未来。。。

Xamarin.Forms ListView OutOfMemoryError在Android上出现异常

是的,我找到了一个解决方案。要遵循的代码。但在此之前,让我来解释一下我做了什么。

因此,我们肯定需要自己掌握材料来处理图像及其底层资源(位图或可绘制,不管你怎么称呼它)。基本上,它归结为处理本机"ImageRenderer"对象。

现在,无法从任何地方获得对该ImageRenderer的引用,因为要做到这一点,需要能够调用Platform.GetRenderer(…)。由于"Platform"类的作用域声明为"internal",因此无法访问该类。

因此,我别无选择,只能对Image类及其(Android)Renderer进行子类,并从内部销毁该Renderer本身(传递"true"作为参数。不要尝试使用"false")。在渲染器中,我挂起页面消失(在TabbedPage的情况下)。在大多数情况下,页面消失事件不会起到很好的作用,例如当页面仍在屏幕堆栈中,但由于另一个页面被绘制在其顶部而消失时。如果您处理图像,当页面再次被揭开(显示)时,它将不会显示图像。在这种情况下,我们必须挂接主导航页面的"Popped"事件。

我已经尽力解释了。其余的-我希望-你将能够从代码:

这是PCL项目中的图像子类。

using System;
using Xamarin.Forms;
namespace ApplicationClient.CustomControls
{
    public class LSImage : Image
    {
    }
}

以下代码在Droid项目中。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Android.Util;
using Application.Droid.CustomControls;
using ApplicationClient.CustomControls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
    [assembly: ExportRenderer(typeof(ApplicationClient.CustomControls.LSImage), typeof(LSImageRenderer))]
    namespace Application.Droid.CustomControls
    {
        public class LSImageRenderer : ImageRenderer
        {
            Page page;
            NavigationPage navigPage;
            protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
            {
                base.OnElementChanged(e);
                if (e.OldElement == null)
                {
                    if (GetContainingViewCell(e.NewElement) != null)
                    {
                        page = GetContainingPage(e.NewElement);
                        if (page.Parent is TabbedPage)
                        {
                            page.Disappearing += PageContainedInTabbedPageDisapearing;
                            return;
                        }
                        navigPage = GetContainingNavigationPage(page);
                        if (navigPage != null)
                            navigPage.Popped += OnPagePopped;
                    }
                    else if ((page = GetContainingTabbedPage(e.NewElement)) != null)
                    {
                        page.Disappearing += PageContainedInTabbedPageDisapearing;
                    }
                }
            }
            void PageContainedInTabbedPageDisapearing (object sender, EventArgs e)
            {
                this.Dispose(true);
                page.Disappearing -= PageContainedInTabbedPageDisapearing;
            }
            protected override void Dispose(bool disposing)
            {
                Log.Info("**** LSImageRenderer *****", "Image got disposed");
                base.Dispose(disposing);
            }
            private void OnPagePopped(object s, NavigationEventArgs e)
            {
                if (e.Page == page)
                {
                    this.Dispose(true);
                    navigPage.Popped -= OnPagePopped;
                }
            }
            private Page GetContainingPage(Xamarin.Forms.Element element)
            {
                Element parentElement = element.ParentView;
                if (typeof(Page).IsAssignableFrom(parentElement.GetType()))
                    return (Page)parentElement;
                else
                    return GetContainingPage(parentElement);
            }
            private ViewCell GetContainingViewCell(Xamarin.Forms.Element element)
            {
                Element parentElement = element.Parent;
                if (parentElement == null)
                    return null;
                if (typeof(ViewCell).IsAssignableFrom(parentElement.GetType()))
                    return (ViewCell)parentElement;
                else
                    return GetContainingViewCell(parentElement);
            }
            private TabbedPage GetContainingTabbedPage(Element element)
            {
                Element parentElement = element.Parent;
                if (parentElement == null)
                    return null;
                if (typeof(TabbedPage).IsAssignableFrom(parentElement.GetType()))
                    return (TabbedPage)parentElement;
                else
                    return GetContainingTabbedPage(parentElement);
            }
            private NavigationPage GetContainingNavigationPage(Element element)
            {
                Element parentElement = element.Parent;
                if (parentElement == null)
                    return null;
                if (typeof(NavigationPage).IsAssignableFrom(parentElement.GetType()))
                    return (NavigationPage)parentElement;
                else
                    return GetContainingNavigationPage(parentElement);
            }
        }
    }

最后,我在PCL项目中将命名空间中的应用程序名称更改为ApplicationClient,在Droid项目中将其更改为Application.Droid。您应该将其更改为您的应用程序名称。

此外,在Renderer类末尾的几个递归方法中,我知道我可以将它组合成一个Generic方法。问题是,当需要的时候,我一次建造一个。所以,这就是我离开的方式。

快乐编码,

Avrohom

另一组可能有帮助的步骤如下:

安卓系统上似乎存在内存泄漏,涉及带有自定义单元格的列表视图。我做了一些调查,发现如果我在我的页面上做了以下操作:

    protected override void OnAppearing()
    {
        BindingContext = controller.Model;
        base.OnAppearing();
    }
    protected override void OnDisappearing()
    {
        BindingContext = null;
        Content = null;
        base.OnDisappearing();
        GC.Collect();
    }

将清单中的android选项设置为使用大型堆(android:largeHeap="true"),最后使用新的垃圾回收器(在environment.txt中,MONO_GC_PARAMS=bridgeimplementation=new)这些因素的结合,似乎解决了崩溃的问题。我只能假设这只是因为将things设置为null有助于GC处理元素,大的堆大小为GC争取了时间,以及新的GC选项有助于加速收集本身。我真诚地希望这能帮助其他人。。。

在Visual Studio 2015中,转到调试选项>.droid属性>Android选项>高级并将Java最大堆大小设置为1G