2012-11-13 29 views
1

我正在开发一款适用于Android的Mono应用程序。无法击败'内存不足'异常

我一直在努力解决过去几天内存不足的问题,并开始失去希望!

我有一个ListView显示200到600个项目的任何东西。这些项目由位图缩略图和一些文本组成。

我异步使用的AsyncTask位图解码,这里是代码:

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

我开始使用这种方法的列表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

如果你能击败他们... ...购买更多的内存。 :) – gdoron

回答

1

我对Android列表的工作原理的理解是,它在列表项视图离开屏幕时处理掉,但感觉好像没有发生!

这是不正确的。

Android的功能是坚持一组项目视图,它试图在屏幕关闭后再次使用它们。这是convertView参数的用途。

我看不到你的Adaptor代码发布在问题中,所以我不知道你的代码是使用convertView参数,但我猜想它应该在convertView的情况下是:

  • 应该取消任何现有的异步获取图像/转换
  • 它应该开始一个新的

的MvvmCross代码可能有点太复杂,你作为一个重ference/example here,但你至少可以看到使用中的convertView - 参见protected virtual View GetBindableView(View convertView, object source, int templateId)