我对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)设计一个非常简单的持有人类别很重要,但这还不够。
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;
}
无论您的映像是否是本地映像,这一点都不会有所不同,您必须事先将其预先加载到共享给Adapter的类中(在我的情况下称为数据)。
编辑:
您也可以按照Skall的建议添加代码来处理变量。
List<CompactedData> data;
public PreviewDishesAdapter(List<CompactedData> data)
{
this.data = data;
}
4)最后,如果您的App要多次使用相同的适配器或类似的适配器以及图像,则此代码在每次活动中都是至关重要的,以避免任何可能的崩溃:
public override void OnBackPressed()
{
base.OnBackPressed();
GC.Collect();
Finish();
}
您必须执行垃圾收集器并完成活动,如果您将其保持打开状态,则随时都有新的内存不足。
如果我们大多数人都考虑了所有这些要点,我们可以避免使用“大堆”或修改Google不推荐的其他要点,这是类似主题中非常常见的建议。
最后要考虑的是,如果您要拥有大量图像,则最好创建一些缩略图以减少ListView的加载,这将使您加载更多数据而没有很多问题,因为即使是RecyclerView可能会导致记忆不足;尤其是在使用选项卡或不同的片段时,因为您无法有效预测何时或如何执行垃圾收集器。
您可以使用ImageResizer之类的应用轻松创建它们,只需单击几下,即可避免不必要的麻烦。