c# - 滚动时 Xamarin.Android 中的 ListView 内存不足

标签 c# android listview xamarin xamarin.android

我是 Xamarin.Android 的新手,我一直在阅读不同的网站和解决方案,但其中大部分是 Java 的,或者他们谈到了一些没有良好文档的库。我的主要问题是,当我滚动应用程序时,它有时会突然崩溃并重新启动,并抛出一条消息内存不足。我当前的适配器具有以下代码:

public override View GetView(int position, View convertView, ViewGroup parent)
{
    var inflater = LayoutInflater.From(parent.Context);
    var view = inflater.Inflate(Resource.Layout.DishesListItem, parent, false);

    var lblTitle = view.FindViewById<TextView>(Resource.Id.Title);
    var imgThumbnail = view.FindViewById<ImageView>(Resource.Id.Thumbnail);

    lblTitle.Text = data[position].name;

    try
    {
        using (StreamReader sr = new StreamReader(Application.Context.Assets.Open(data[position].image)))
        {
            Drawable d = Drawable.CreateFromStream(sr.BaseStream, null);
            imgThumbnail.SetImageDrawable(d);
        }
    }
    catch
    {
        imgThumbnail = null;
    }

    return view;
}

我想澄清一下,我所有的图像都存储在本地,我没有从互联网上下载任何东西,也没有访问 SD 卡、图片文件夹等。我的图像位于 Assets 文件夹因为它们链接到 SQLite 数据库和一些 ID。

目前,我只是在 OnCreate 方法上加载一次适配器。

我找到了这个解决方案,但它并不总是有效: Tips & Tricks for Highly Performing Android ListViews

我注意到,如果您从一个 Activity 移动到另一个 Activity,然后又回到 ListView 所在的 Activity 并开始上下滚动,它只会再次崩溃。

有没有人经历过?如果你有,你有什么我可以改变的建议吗?

最佳答案

在我的研究取得成功后,我想分享我的解决方案,希望在不久的将来能帮助到更多的开发者。我认为这些注释在 C# 和 Java 中很有用。

1)设计一个极其简单的holder类很重要,但这还不够。

    private class MyViewHolder : Java.Lang.Object
    {
        public TextView lblTitle { get; set; }
        public ImageView imgThumbnail { get; set; }
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        var inflater = LayoutInflater.From(parent.Context);
        MyViewHolder holder = null;
        var view = convertView;

        if (view != null)
            holder = view.Tag as MyViewHolder;

        if (holder == null)
        {
            holder = new MyViewHolder();
            view = inflater.Inflate(Resource.Layout.DishesListItem, parent, false);
            holder.lblTitle = view.FindViewById<TextView>(Resource.Id.Title);
            holder.imgThumbnail = view.FindViewById<ImageView>(Resource.Id.Thumbnail);
            view.Tag = holder;
        }

        holder.lblTitle.Text = data[position].name;

        try
        {
            holder.imgThumbnail.SetImageDrawable(data[position].image);
        }
        catch
        {
            holder.imgThumbnail = null;
        }

        return view;
    }

2) 您需要向适配器发送一个非常简单的类,其中仅包含要加载的信息。如果你这样做,你不应该发送更多或更少的信息,将会出现新的内存不足。

public class CompactedData
{
    public int id { get; set; }
    public string name { get; set; }
    public Drawable image { get; set; }
}

3) 我的直觉告诉我应该进一步优化类并且我需要停止在 Adapter 中加载任何图像,如果这样做的话 holder 会有点低效而且它是真的;适配器不够好,如果您想获得最大效率,您需要预加载要添加到 holder 类的所有图像。

这部分代码是我经验中效率最低的,迟早会造成新的内存不足。

try
{
    using (StreamReader sr = new StreamReader(Application.Context.Assets.Open(data[position].image)))
    {
        using (var d = Drawable.CreateFromStream(sr.BaseStream, null))
        {
            imgThumbnail.SetImageDrawable(d);
        }
    }
}
catch
{
    imgThumbnail = null;
}

如果您的图像是本地的与否,这一点不会有所不同,您必须将其预加载到您要共享到适配器的类中之前(在我的例子中称为数据)。

编辑: 您也可以按照 Skall 的建议添加代码来处理变量。

    List<CompactedData> data;

    public PreviewDishesAdapter(List<CompactedData> data)
    {
        this.data = data;
    }

4) 最后,如果您的应用要多次使用同一个适配器或类似的图像适配器,则此代码在每个 Activity 中都是重要的避免任何可能的崩溃:

    public override void OnBackPressed()
    {
        base.OnBackPressed();
        GC.Collect();
        Finish();
    }

必须执行垃圾收集器完成 Activity,如果您保持打开状态,将会发生在任何时候成为新的内存力不足

如果我们大多数人都考虑到所有这些点,我们就可以避免使用Large Heap 或修改 Google 不推荐的其他点,这些都是类似主题中非常常见的建议。

最后一个想法,如果您要拥有大量图像,最好创建一些缩略图 以减少 ListView 的加载,这将允许您加载更多数据而不会出现很多问题,因为即使是 RecyclerView 也可能会导致内存不足;特别是当您使用Tabs 或不同的Fragments 时,因为您无法有效预测何时或如何执行垃圾收集器。

您可以使用一些应用程序轻松创建它们,例如 ImageResizer ,只需单击几下,您就可以避免不必要的麻烦。

关于c# - 滚动时 Xamarin.Android 中的 ListView 内存不足,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40799931/

相关文章:

C# 无法从 DbContext 中删除对象

java - Android 上的最佳方式或类似的 lambda 表达式或 linq(eclipse)

android - Admob 想要 640,100 有 : 480, 654 - 为什么没有交付 480 尺寸的横幅?

c# - 如何在 ListView 中显示对象列表属性

c# - 在 ASP.net Web 应用程序中使用多种编程语言

c# - INotifyPropertyChanged 在 silverlight c# 中频繁调用时出错

c# - 使用自定义小时和分钟构造日期时间

android - 使用 Observables 时如何忽略 Retrofit 答案?

java - 平板电脑上的 ListView

.net - 填充 ListView 多列