滚动时,ListView在Xamarin.Android中缺少内存

时间:2016-11-25 07:41:19

标签: 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的活动,你开始向上和向下滚动,它只会再次崩溃。

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

3 个答案:

答案 0 :(得分:2)

在我的研究提供了成功的结果后,我想分享我的解决方案,我希望在不久的将来帮助许多开发人员。我认为这些注释在 C#和Java中很有用。

1)设计一个非常简单的持有者类非常重要,但这还不够。

    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)我的直觉告诉我,我应该更多地优化课程,我需要停止在适配器中加载任何图像,如果这样做,持有者将会有点效率低下它是真的;适配器不够好,如果你想要最高效率,你需要预先加载你要添加到持有者类的所有图像。

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

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;
}

如果您的图片是本地图片,那么这一点不会有所不同,您必须将其预先加载到您要与适配器共享的课程中 strong>之前(在我的情况下被称为数据)。

修改 您可以像Skall建议的那样添加代码来处理变量。

    List<CompactedData> data;

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

4)最后,如果您的应用程序要使用相同的适配器或带有图像的类似适配器,则此代码在每个活动中按顺序至关重要避免任何可能的崩溃:

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

必须执行垃圾收集器完成 活动,如果您将其保持打开状态在任何时候成为新的 缺乏记忆

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

最后一个想法,如果您要拥有大量图片,最好创建一些缩略图以减少加载量ListView,这将允许您加载更多数据,没有太多问题,因为即使RecyclerView可能会产生内存不足;特别是如果您正在使用标签或不同的片段,因为您无法有效地预测何时或如何执行垃圾收集器。

您可以使用ImageResizer之类的应用轻松创建它们,只需点击几下即可避免不必要的麻烦。

答案 1 :(得分:2)

我们遇到了类似的问题,尤其是在快速滚动listview时,除了优化listView之外我们做的是添加此应用程序设置:

//You can specify additional application information in this attribute
    [Application(LargeHeap = true)]
    public class MainApplication : Application, Application.IActivityLifecycleCallbacks
    { ...

在MainApplication.cs上面:

 <application android:label="YourAppName" android:largeHeap="true" android:icon="@drawable/icon"></application>

它的作用:如果为true,则应使用大型Dalvik堆创建进程;否则,将使用默认的Dalvik堆创建进程。

您也可以在AndroidManifest.xml中设置它。

def a(choice_category, choice_difficulty)

答案 2 :(得分:1)

使用Android图片时,您必须非常小心。我没有测试过这个,但是drawable有可能造成内存泄漏。你可以试试这个是否有任何帮助:

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