我是 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/