Android 上的 Xamarin.Forms ListView OutOfMemoryError 异常 [英] Xamarin.Forms ListView OutOfMemoryError exception on Android

查看:14
本文介绍了Android 上的 Xamarin.Forms ListView OutOfMemoryError 异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Anyone ever tried A Xamarin.Forms Listview with an ItemTemplate containing a Image view? Now, what happens when ListView contains ca 20 or more rows?

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

As for me, I have a .png file of around 4K in size loaded into the Image view. Got a maximum of 9 - 12 rows shown before application crashed with a OutOfMemoryError. After requesting a large heap in the android Manifest, the app crashes after 60 - 70 rows.

我知道 Xamarin 正在推广使用 BitmapFactory 类来缩小位图,但这不适用于(开箱即用)Xamarin Forms Image View.

I know that Xamarin is promoting the use of the BitmapFactory class to scale down bitmaps, but this is not applicable (out of the box) for the Xamarin Forms Image View.

我正在尝试修改 ImageRenderer 的子类,以查看是否可以添加 BitmapFactory.Options 属性以及这是否可以解决问题.

I'm about trying to fiddle with a Sub Class of the ImageRenderer to see if I can add a BitmapFactory.Options property and if this will solve the problem.

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

Also, I might need to check out if Xamarin.Forms does dispose (recycle) the contained bitmap after the ViewCell is being scrolled of the screen.

在开始此旅程之前,我非常希望得到任何可以使此过程变得更轻松的评论或更简单的解决方案,从而认为此过程是不必要的.

Before setting out on this journey, I would be very keen to get any comments that could make this easier or a simpler solution that would deem this process unnecessary.

期待...

推荐答案

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

Yes, I found a solution. Code to follow. But before that, let me explain a bit what I have done.

因此,肯定需要我们自己动手处理图像及其底层资源(位图或可绘制对象,无论您想如何称呼它).基本上,它归结为处置本机ImageRenderer"对象.

So, there's definitely a need to take maters in our own hands to dispose the image and its underlying resources (bitmap or drawable, however you want to call it). Basically, it comes down to dispose the native 'ImageRenderer' object.

现在,无法从任何地方获取对该 ImageRenderer 的引用,因为为此,需要能够调用 Platform.GetRenderer(...).无法访问平台"类,因为其范围被声明为内部".

Now, there's no way to obtain a reference to that ImageRenderer from anywhere because in order to do so, one need to be able to call Platform.GetRenderer(...). Access to the 'Platform' class is inaccessible since its scope is declared as 'internal'.

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

So, I have been left with no choice other than to sub-class the Image class and its (Android) Renderer and destroy this Renderer itself from inside (passing 'true' as argument. Don't try with 'false'). Inside the Renderer I hook on to page disappear (In case of a TabbedPage). In most situations the Page Disappear event will not serve the purpose well, such as when the page is still in the screen stack but disappears due to a another page is being drawn on Top of it. If you dispose the Image(s) than, when the page gets uncovered (shown) again, it will not display the images. In such case we have to hook on the the main Navigation Page's 'Popped' event.

我已经尽力解释了.其余的 - 我希望 - 您将能够从代码中获得:

I have tried to explain to the best I could. The rest - I hope - you will be able to get from the code:

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

This is the Image Sub-Class in the PCL Project.

using System;

using Xamarin.Forms;

namespace ApplicationClient.CustomControls
{
    public class LSImage : Image
    {
    }
}

以下代码在 Droid 项目中.

The following code is in the Droid project.

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".您应该将其更改为您的应用名称.

Finally, I have changed the Name of the Application in the namespace to 'ApplicationClient' in the PCL project and to 'Application.Droid' in Droid project. You should change it to your app name.

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

Also, the few recursive methods at the end of the Renderer class, I know that I could combine it into one Generic method. The thing is, that I have build one at a time as the need arose. So, this is how I left it.

快乐编码,

阿罗霍姆

这篇关于Android 上的 Xamarin.Forms ListView OutOfMemoryError 异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆