我是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的活动,你开始向上和向下滚动,它只会再次崩溃。
有没有人以前经历过它?如果你有,你有建议我可以改变什么?
答案 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);
}
}