我正在使用Mono for Android开发应用程序。
过去几天我一直在努力解决内存异常问题,并开始失去希望!
我有一个ListView,显示200到600个项目。这些项目由位图缩略图和一些文本组成。
我使用AsyncTask异步解码Bitmap,这是代码:
public class BitmapWorkerTask : AsyncTask
{
private WeakReference imageViewReference;
public string thisURL = "";
private int sampleSize = 0;
private int reqHeight = 0;
private int reqWidht = 0;
public BitmapWorkerTask(ImageView imageView, int pSampleSize, int pReqWidth, int pReqHeight)
{
//_____________________________________________________________________
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference(imageView);
reqHeight = pReqHeight;
reqWidht = pReqWidth;
sampleSize = pSampleSize;
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] @params)
{
string strUrl = @params[0].ToString();
try
{
return DecodeSampleBitmapFromStream(strUrl, reqWidht, reqHeight);
}
catch (Exception ex)
{
return null;
}
}
protected override void OnPostExecute(Java.Lang.Object result)
{
base.OnPostExecute(result);
if (IsCancelled)
{
result = null;
Log.Debug("TT", "OnPostExecute - Task Cancelled");
}
else
{
Bitmap bmpResult = result as Bitmap;
if (imageViewReference != null && bmpResult != null)
{
ImageView view = imageViewReference.Target as ImageView;
if (view != null)
{
view.SetImageBitmap(bmpResult);
}
}
}
}
public static int CalculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight)
{
//_____________________________
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
if (width > height)
{
inSampleSize = (int)Math.Round((float)height / (float)reqHeight);
}
else
{
inSampleSize = (int)Math.Round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
public static Bitmap DecodeSampleBitmapFromStream(string URL, int reqWidth, int reqHeight)
{
URL url = new URL(URL);
try
{
//______________________________________________________________
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeStream(url.OpenConnection().InputStream, null, options);
//______________________
// Calculate inSampleSize
options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight);
//____________________________________
// Decode bitmap with inSampleSize set
options.InJustDecodeBounds = false;
return BitmapFactory.DecodeStream(url.OpenConnection().InputStream, null, options);
}
catch (Exception ex)
{
return null;
}
finally
{
url.Dispose();
}
}
我使用此方法从Lists GetView()函数启动此AsyncTask:
public void loadBitmap(string url, ImageView imageView)
{
if (Common.cancelPotentialWork(url, imageView))
{
BitmapWorkerTask task = new BitmapWorkerTask(imageView, 2, 80,80);
AsyncDrawable asyncDrawable = new AsyncDrawable(null, null, task);
imageView.SetImageDrawable(asyncDrawable);
task.Execute(url);
}
}
一切都按预期工作了一段时间,但是如果我不断在我的列表中上下滚动,我最终会开始获取OutOfMemoryException
并且应用程序崩溃了。我对Android列表如何工作的理解是它在屏幕移动时处理了ListItem
个视图,但感觉好像没有发生这种情况!
感觉就像我正在解码的所有那些位图,因为我滚动列表是出于什么原因被保存在内存中?我可以在这里找到什么阻止这些位图被处理掉?我在哪里可以实现对Bitmap.Recycle()
的调用以确保清除位图?
我做了一个测试,我在每次调用GetView时调用了GC.Collect,它确实让我的内存使用率保持相当一致,但我知道这不应该是必需的,它会影响滚动性能。
为什么当我在没有调用GC.Collect()
的情况下滚动我的列表时,我没有看到那些垃圾收集消息,表明该系统实际上正在进行常规收集?
感谢任何帮助,我失去了代码的意愿!
答案 0 :(得分:1)
我对Android列表如何工作的理解是它在屏幕上移动时处理了List项目视图,但感觉好像没有发生这种情况!
这不正确。
Android所做的是保持项目视图集,它会在屏幕关闭后尝试重用它们。这就是convertView参数的用途。
我无法在问题中看到你的适配器代码,所以我不确定你的代码是什么用于使用convertView参数,但我猜它在convertView的情况下应该做的是:< / p>
MvvmCross代码在这里作为参考/示例可能有点过于复杂,但您至少可以在MvxBindableListAdapter.cs中看到使用的convertView - 请参阅protected virtual View GetBindableView(View convertView, object source, int templateId)